How do I migrate from JAAS to an identity-centric security model in Java Agents 5.x?
The purpose of this article is to help you migrate from Java® Servlet Security for JAAS applications to an identity-centric security model in conjunction with ForgeRock Access Management.
Background
This article is intended to assist organizations to evolve Java Servlet Security for JAAS applications to an identity-centric security model in conjunction with ForgeRock Access Management.
The support for JAAS (Java Authentication and Authorization Service) was removed in Java Agents 5. See Removed Functionality for further information.
Modernizing Java Servlet Security
Modern applications are externalizing their security for various reasons:
- Decouples application from security concerns so application development can be handled by other departments or external companies.
- Allows for the central management of policies.
- Policies can be changed without having to change applications.
This means JAAS - which is a Servlet container standard for authentication and authorization - has become less relevant; it runs within the application and mostly duplicates what Java Agents are already doing.
Advantages of Identity-Centric Security
- Complex policies
Servlet security constraints are limited in terms of what can be expressed; only roles and data transport can be specified. Roles are semantically equivalent to LDAP groups and data transport enforce/deny TLS variants. AM policies are far richer, and although role/group constraints can easily be specified, other constraints are available, for example, active session time, authentication level, IP address, time of day, and so on. These additions allow more complex use cases to be implemented.
- Centralized authorization policies
JAAS security constraints are defined in each and every application’s WEB-INF/web.xml and are therefore part of the application’s deliverable. This means, in order to change a constraint, the application has to be rebuilt and redeployed. This can be a problem if the entity controlling security is not the same as the one delivering the application (assuming a business delegates application development/maintenance to another company or business unit) - each change request has an associated cost and delay. With centralized policies, businesses regain security management, with policies being centralized on the AM server. All changes made within AM are picked up automatically by the Agents, which apply the new rules without intervention or redeployment. These changes are also auditable.
- Extensible to non-Servlet applications
As servlet security is bound to Servlet web containers such as Apache Tomcat™, Jetty® and so on, only Servlet-based web applications can be protected. If any other type of web application is deployed, it cannot be protected in the same way. With centrally managed policies, it becomes possible to normalize the way security is handled for all web applications. This does not necessarily mean Java Agents are the only answer; ForgeRock provides other products (Web Agents and Identity Gateway) which are adapted for different deployments, all handling AM policies.
Inconveniences of Identity-Centric Security
- No Servlet security integration
Without JAAS, there is no servlet security integration, so all JAAS features are disabled. Some of these features can be replicated through AM policy, some can be implemented by manipulating the Agent and AM configuration settings, some will necessitate code changes and some may need to be removed. The available options are covered in more detail in the Migration Steps section.
- No security context propagation
Servlet Security creates a security context that stores security information and propagates it throughout the execution of the request. This could cross container boundaries, meaning the Principal information could be conveyed to other Java EE components such as Enterprise Java Beans (EJBs). Without JAAS, this is no longer possible as the Principal is no longer computed.
Migration Steps
The following sections provide information on migrating specific JAAS features:
- Security Constraints
- HttpServletRequest.isUserInRole()
- HttpServletRequest.getRemoteUser()
- HttpServletRequest.getUserPrincipal()
Security Constraints
JAAS security places constructs like these in a web application's web.xml file:<security-constraint> <web-resource-collection> <web-resource-name>items</web-resource-name> <url-pattern>/sale/items/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>PARTNER</role-name> </auth-constraint> </security-constraint>This example describes a constraint on a URL resource (in url-pattern) for some HTTP verbs (GET and POST) where only authenticated users with the PARTNER role are granted access.
The same security constraints can be achieved with an equivalent AM policy, for example:
This policy rule grants anyone in the group "Accessors" GET and POST access to any resource under sales/items (providing, of course, there are no other more specific rules applying to resources in sales/items which contradict this).
Users requiring access to these resources have to be added to the specified group.
See Policies for further information.
HttpServletRequest.isUserInRole()
Since Agents 5 no longer creates the Principal object, it is not possible to directly interrogate the container about the Principal's attributes with the isUserInRole() method.
The Java Agent isUserInRole() support was implemented as a mapping function from the role name to an LDAP group. So effectively, when asked if a user has the role "ADMIN", it was translated into an inspection of the user’s isMemberOf attribute with the role name mapped to the LDAP DN of the group. This now has to be implemented in each application.
When an unauthenticated user accesses a resource, one of the steps the Agent performs is to retrieve profile information about that user. You can configure which attributes are returned to the Agent in AM as outlined below. These changes not only make the group membership information available to the Agent via the user profile attributes, but also propagate it from the Agent to whatever servlet is next in the chain via headers, cookies or request attributes.
Modify the Identity Store configuration to include the ismemberof attribute in the user profile
- Navigate to: Realms > [Realm Name] > Identity Stores > [Identity Store Name] and select the User Configuration tab. You will see something like this:
- Enter
ismemberof
in the LDAP User Attributes field. - Click Save Changes.
- Restart the web application container in which AM runs to apply these configuration changes.
Configure the Agent to map the ismemberof information
- Navigate to: Realms > [Realm Name] > Applications > Agents > [Agent Name] and select the Applications tab.
- In the Profile Attribute Mapping field, enter
ismemberof
in the Key field, enterGROUP_INFO
in the corresponding Value field and click Add. - Click Save Changes.
Configure the Agent to retrieve the user profile information (Java Agents 5.7 and later)
Since Java Agents 5.7, the Agent does not retrieve user profile information unless configured to do so. This means you will need to set the profile attribute fetch mode within the Agent profile to a value other than NONE:
- Navigate to: Realms > [Realm Name] > Applications > Agents > [Agent Name] and select the Applications tab.
- Select one of the following options in the Profile Attribute Fetch Mode field:HTTP_HEADER REQUEST_ATTRIBUTE HTTP_COOKIESee Profile Attribute Fetch Mode for further information about these options.
- Click Save Changes.
Access this data within the application code
You can then access this data within the application code and re-implement isUserInRole with something like the following pseudo code:boolean isUserInGroup(HttpServletRequest request, String groupDn) { Collection<String> groups = request.getAttribute("GROUP_INFO"); return groups.contains(groupDn); }
HttpServletRequest.getRemoteUser()
Once user profile information is retrieved from AM, you will find it includes the universalid and dn of the user, for example:{ "realm": "/", "username": "jdoe", "uid": [ "jdoe" ], "universalid": [ "id=jdoe,ou=user,dc=am,dc=forgerock,dc=org" ], "objectClass": [ "iplanet-am-managed-person", ... "deviceProfilesContainer" ], "dn": [ "uid=jdoe,ou=people,dc=am,dc=forgerock,dc=org" ], "sn": [ "jdoe" ], "cn": [ "jdoe" ], "ismemberof": [ "cn=Accessors,ou=groups,dc=am,dc=forgerock,dc=org" ], "createTimestamp": [ "20210521081634Z" ] }
The universalid can be used to uniquely identify the user.
All of the profile information attributes (except kbaInfo
and objectClass
) are made available in the Servlet request’s attributes (using their respective names).
The following pseudo code shows how to retrieve this information from the request attributes:String getRemoteUser(HttpServletRequest request) { return request.getAttribute("universalid"); }
HttpServletRequest.getUserPrincipal()
As the Java Agent doesn't create the Principal object, this method has no replacement.
The getRemoteUser() technique works as a replacement if the only thing that was read from the Principal was its name.
See Also
Related Issue Tracker IDs
AMAGENTS-4196 (JASPA: When profile attribute is not found agent should continue authorization.)