Not so long ago I’ve been working on JBoss v7 agent, so let me try to sum up some of my findings with Java EE agents in general.
A Java EE agent basically stands from 4 main components:
- Container specific JAAS integration
- Java EE Filter implementation
- Agent application
Let’s go through each one of these and see how they really work.
As its name suggest, the agent installer helps to install the Policy Agent (PA) on the chosen Java EE container. The main behavior of the installer is defined within config/configure.xml file. This file is basically a big XML descriptor of the followings:
- interactions – what should be asked from the user when performing install/custom-install/migrate/uninstall
- tasks – what tasks needs to be performed as part of the agent installation, things like backup, creating directory layout, etc
So basically this XML describes how the installer should work in general. Now let’s go even more deeper…
During an agent installation all user input is being stored in a thing called IStateAccess (well sometimes there is a persistent=”false” interaction which isn’t persistent, but that part I don’t fully grasp yet ).
As part of an interaction it is possible to set up different validators (even custom ones), so for example this makes it possible that a given agent is actually installed on a supported container version. For example with JBoss we are running “JBOSS_HOME/bin/instancename.(bat|sh) –version” command to get back the version number of the JBoss instance, parse it and if it matches, we let the installer further.
Tasks are usually just a well defined “change” that needs to be performed by the installer. Implementing a task is not just performing a given change, rollback is just as important part of it. In order to make an agent work, it is very likely that some container specific configuration file needs to be modified (for example to configure/enable the JAAS integration), the necessary file paths are all calculated based on the user input during the interactions (IStateAccess).
Usually an agent installer performs the following tasks:
- creating backups for the container’s configuration files
- perform modifications to the container configuration
- create agent directory layout (i.e. creating the Agent_00x folder structure)
- encrypt the agent profile’s password and save it in the agent bootstrap config
- generate the configuration files based on the values provided to the installer
- in certain scenarios: deploy agentapp.war in the container
- in case of custom-install: create agent profile if needed
As you can probably see there is nothing super magical about the agent installer, its sole purpose is basically to make the deployer’s life easier by automating this whole process.
I feel we heard enough about the installer for now, so let’s go a bit further and see what the JAAS integration is really all about.
Container specific JAAS integration
JAAS in general isn’t that new really, it’s been part of JRE 1.4 already, so let’s look at it very shortly (here is a longer version):
- You can implement a custom authentication module
- You can define authentication “chains” using built-in/custom modules in a configuration file
Well hopefully this concept doesn’t sound too new for you, since the whole OpenAM authentication system is based on JAAS.
As an extra, application servers tend to have the concept of “JAAS realm”, which is basically a collection of users (to be sparse as much as possible). Unfortunately there is not much standard around implementing JAAS support for application servers, so this part of the agents is different per container (and sometimes even across container versions).
Advantages of JAAS
Well the main advantage of JAAS is that it can help making the OpenAM integration as less intrusive as possible, but there are others as well:
- Retrieving the logged in user’s name is quite simple, you can call request#getRemoteUser() or request#getUserPrincipal().
- By setting up Java EE Security it is possible to define roles in the application’s descriptor files and later on in your application you can just simply check against these using request#isUserInRole(String) (authorization).
- In case you later on decide to use an LDAP based JAAS module instead, the idea is that you can simply switch out the JAAS login module (of course you’ll need to configure it to work similarly to OpenAM), without actually modifying any part of your application code.
- While this is already good enough, I think JAAS really pays off when EJBs are used in applications. In that case you can use the @DeclareRoles and @RolesAllowed annotations and magically your EJB method will be protected, only those in the configured role can actually invoke the method (anything else results in failure), which sounds quite neat.
So what about agents?
The Java EE agents support JAAS, they integrate well with the container and within the agent configuration it is possible to set up role mapping based on pretty much any arbitrary data (session property, profile attribute). To enable JAAS support though you must ensure that you configure the agent in J2EE_POLICY or ALL mode. While it is possible to define security-constraints in web.xml to protect access to pages, usually that’s a bit cumbersome, instead it’s probably simpler to just define OpenAM policies (i.e. use the agent in ALL mode), and only use JAAS for the previously described goodies.
Java EE Filter implementation
The agent itself is implemented as a Java EE filter, which is (obviously) standard, so this part is common for all the agents and as such it is part of the agent SDK. When the filter intercepts a given request, it will basically go through a list of “TaskHandlers” and execute them in a predefined order. If a given TaskHandler returns with an AmFilterResult, then the processing stops and the result will be handled. Here is a small example subset of the available TaskHandlers:
- CDSSOTaskHandler – detects if the user needs to authenticate using CDSSO and redirects to CDCServlet if necessary
- CDSSOResultTaskHandler – processes the incoming LARES response and creates the cookie on the application’s domain
- URLPolicyTaskHandler – checks the currently visited URL against configured URL policies in OpenAM, and blocks access if necessary
- InitialPDPTaskHandler – saves POST data if the user hasn’t authenticated yet, so later on the POST can be resubmitted once authentication took place.
- XSSDetectionTaskHandler – checks incoming request parameters for malicious characters
Since the agent is implemented as a filter, you can see how it can alter the outcome of a given incoming request. If the user doesn’t have session yet, it will prevent further processing of the page, and redirect straight to OpenAM instead. This however means that in order to protect the application fully, you must set the agent’s filter as the first one, otherwise it may be possible that a filter earlier in the chain alters the response, or returns earlier, which could basically prevent the agent to protect the application properly.
And here is the last component which is pretty simple, the agent application. The purpose of this application is to provide a unique contextroot for the agent, so the agent can receive notifications from OpenAM, and also LARES responses in case of CDSSO. To be fair the agentapp only has the agent’s filter defined, and that basically handles everything. If there is an incoming notification, then the filter just checks if the requested URL is actually the notification URL, and processes it if so.
As a sidenote I would mention that deploying agentapp.war is not necessary when using global web.xml with Tomcat 6. That is simply because the global web filter already includes the agent filter, hence any incoming request to the /agentapp context will be catched by the filter already.
Agents are fun, but that doesn’t mean they are not complex.