Adelia provides a Pac4J-based extension module ("OpenID Connect" support by implementing the "authorization code flow"). This extension module can be used with Adelia Web and Adelia Cloud.
https://github.com/pac4j/jee-pac4j
This module is based on a set of filters derived from the basic implementation of Pac4J filters with the addition of an Adelia-specific default behavior and configuration.
It also includes a specific module for Adelia Cloud used to delegate role management (isUserInRole) to Pac4J.
The extension configuration file (security.yml)
This file contains the extension parameters. By default, it is searched for in the "WEB-INF/conf" directory of the Web application, but can be externalized via the JNDI resource "java:comp/env/url/adeliaSecurityConfig";.
wagonSecurity: callbackUrl: https://domain.com/application/callback accessTokenExpiryAdvance: 30 corsAuthorizer: allowedOrigins: "*" allowedMethods: GET,POST,HEAD,OPTIONS,PUT,DELETE # allowedHeaders: Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization # preflightMaxage: 1800 authorizationGenerator: className: com.hardis.adelia.jee.security.RoleGenerator rolesClaim: resource_access.clientId.roles, realm_access.roles clientsProperties: Oidc.client_name: clientName oidc.id: clientId oidc.secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx oidc.discoveryUri: https://identity_provider.com/pathTo/.well-known/openid-configuration oidc.useNonce: true oidc.preferredJwsAlgorithm: RS256 oidc.scope: openid email profile oidc.clientAuthenticationMethod: client_secret_post
The configuration file contains the following sections:
The "wagonSecurity" section contains the module's general settings:
- callbackUrl is the callback URL to use when OpenID authentication is enabled. It is the absolute URL corresponding to the CallbackFilter (see below).
=>> for example: https://domain.com/application/callback
accessTokenExpiryAdvance is the time before expiry used to determine whether the access token needs to be renewed. The default value is 30 seconds.
=>> in OpenID connect mode, the Adelia security filter "SecurityFilter" will automatically update the access tokens. The identity provider used must support "refresh token";.
The "authorizationGenerator" is used to define the role generation settings. This is an optional section.
The authorization generator is automatically called at the end of authentication. Its purpose is to extract the list of security roles associated with the logged-in user from the identity information.
Adelia provides a default implementation (com.hardis.adelia.jee.security.RoleGenerator) which is used to extract a list of "claim"; roles from user information. A customized implementation can be provided.
The Adelia implementation can also be used to load the username (*USER) from a specific claim (by default with OpenID, the username is extracted from the profile's "preferred_username" claim).
The parameters are:
- className is the class name to use. This must implement the "org.pac4j.core.authorization.generator.AuthorizationGenerator"; Pac4J interface. The default value is "com.hardis.adelia.cloud.security.ext.RoleGenerator";.
- userNameClaim is the name of the claim containing the username (e.g. userNameClaim: email). If this parameter is not specified, the profile's "preferred_username" will be used, or the user ID if there is no profile information.
- rolesClaim is the name of the client containing the list of roles (in the case of the default extension class). You can specify a qualified name and consolidate the reading of several claims by separating the names with commas.
- parseAccessToken is used to search for roles and/or the user name in the access token (if OpenID). By default (false), only the ID token and user information are taken into account by the role generator.
A user implementation is free to define other parameters. For example, the following implementation (className = com.test.CustomRoleGenerator) associates the "wagon-administrator"; role with a configured profile.
package com.test; import org.pac4j.core.authorization.generator.AuthorizationGenerator; import org.pac4j.core.context.WebContext; import org.pac4j.core.profile.CommonProfile; import com.hardis.adelia.jee.security.SecurityConfig; public class CustomRoleGenerator<U extends CommonProfile> implements AuthorizationGenerator<U> { private String adminProfile; public CustomRoleGenerator() { adminProfile = SecurityConfig.getString(SecurityConfig.AUTHORIZATION_GENERATOR, "adminProfile", "admin"); } @Override public U generate(WebContext context, U profile) { for (String role: computeRoles(context, profile)) { profile.addRole(role); } return profile; } private String[] computeRoles(WebContext context, U profile) { return (profile.getUsername().equals(adminProfile))? new String[] { "wagon-administrator", "user" } : new String[] { "user" }; } }
The "corsAuthorizer"; section is used to enable a built-in CORS filter. This is an optional section.
The CORS filter will be enabled if the section is present in the configuration file.
The parameters are:
- allowedOrigins defines the list of origins which are authorized to access the resource. "*" accepts all origins. The default value is "*";.
- allowedMethods defines the list of authorized HTTP methods, separated by commas. The recognized methods are GET, POST, HEAD, TRACE, PUT, DELETE, OPTIONS and PATCH. Not defined by default.
- allowedHeaders defines the list of authorized headers in requests. Not defined by default.
- exposedHeaders defines the list of headers exposed in the responses to the browser. Not defined by default.
- allowCredentials is used to authorize or deny the propagation of cookies, accreditation vectors and SSL certificates. True by default.
The "clientsProperties"; section contains Pac4J client properties. Client configuration is instantiated using standard Pac4J mechanisms ("PropertiesConfigFactory";).
The example file above defines a minimal configuration of the security extension for an OpenID Connect authentication.
- oidc.clientName is an Adelia extension used to force the Pac4J client name instead of using a generated name (this appears in the callback URL which can be validated by the identity server).
- oidc.id is your application's identifier, which is registered with your provider. It is provided by the identity provider.
- oidc.secret is the secret associated with your application. It is provided by the identity provider.
- oidc.discoveryUri is the identity provider configuration endpoint URL.
- oidc.clientAuthenticationMethod is used to force the authentication method with the server (possible values: "client_secret_post";, "client_secret_basic"; or "none";).
Note: by default, the OpenID client uses the "authorisation code flow"; connection mode. The "implicit flow"; mode can be used by adding the following properties:
- oidc.responseType specifies the type of response required from the provider. For the implicit flow, specify the "id_token"; or "id_token token"; value ("code"; by default).
- oidc.responseMode specifies the results transfer mode. For the implicit flow, specify the "form_post"; value (not entered by default).
The complete reference of the "PropertiesConfigFactory"; parameters can be found here (classified by client type): https://www.pac4j.org/docs/config.html.
Note: the Adelia extension does not support multi-profile in Pac4J. The configuration of several clients via the properties is not supported.
The com.hardis.adelia.jee.security.filter.SecurityFilter filter
This filter extends the Pac4J "SecurityFilter"; filter. This filter must be configured if you use the extension.
https://github.com/pac4j/jee-pac4j/wiki/Security-configuration
The Adelia version of this filter adds the following features (used in conjunction with Open ID):
Automatic refresh of the access token if supported by the identity provider.
WagonLoginModule login module support to manage roles in Adelia Cloud.
The filter declares an additional parameter in relation to the original:
- excludeUrlPattern is used to declare a regular expression to indicate the URLs to exclude from processing, and which will therefore not be secured. NB: unlike the filter-mapping "url-pattern" parameter, it is a complex regular expression. In addition, this is evaluated taking into account the query parameters (if the URL is "http://serveur/application/logout.jsp?sessionId=xxxxx", the regular expression is evaluated on "/logout.jsp?sessionId=xxxxx", whereas the filter-mapping url-pattern does not take the parameters into account and is evaluated on "/logout.jsp").
The example below excludes /logout.jsp (which can take parameters, hence the ".*", the scripts and static resources (/js/* and /favicon.ico, potentially loaded by logout.jsp), and the URLs generated by the VaToolBxCloudDownloadFile function (which allows them to be used outside the browser context, in an upload manager for example).
Filter declaration in "web.xml";:
<filter> <filter-name>SecurityFilter</filter-name> <filter-class>com.hardis.adelia.jee.security.filter.SecurityFilter</filter-class> <init-param> <param-name>excludeUrlPattern</param-name> <param-value>/logout.jsp.*|/js/.*|/favicon.ico|/WagonServlet.*action=DOWNLOAD.*</param-value> </init-param> </filter> ... <filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Using with Adelia Cloud:
With Adelia Cloud, it is generally advisable to secure the whole application (<url-pattern>/* </url-pattern>) in the filter configuration. If, however, you only want to secure the cloud application itself and not the resources (e.g. to allow redirection to "logout.jsp"; in the LogoutFilter without generating a new authentication request after logging out), you can use the "excludeUrlPattern" parameter of the filter as described above, or the following settings to secure the application's critical entry points only:
<filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/index.jsp</url-pattern> <url-pattern>/WagonServlet/*</url-pattern> <url-pattern>/WagonSyncServlet/*</url-pattern> <url-pattern>/WagonWS/*</url-pattern> </filter-mapping>
The com.hardis.adelia.jee.security.filter.CallbackFilter filter
This filter extends the Pac4J "CallbackFilter"; filter to load the Adelia configuration only. The parameter settings are identical to the original filter.
https://github.com/pac4j/jee-pac4j/wiki/Callback-configuration
This filter only needs to be configured if "Open ID" is used. If it is configured, it must be declared first in the list of filters when executed (filter-mapping in web.xml). It must be executed before the SecurityFilter.
<filter> <filter-name>CallbackFilter</filter-name> <filter-class>com.hardis.adelia.jee.security.filter.CallbackFilter</filter-class> <init-param> <param-name>renewSession</param-name> <param-value>false</param-value> </init-param> </filter> ... <filter-mapping> <filter-name>CallbackFilter</filter-name> <url-pattern>/callback</url-pattern> </filter-mapping>
The com.hardis.adelia.jee.security.filter.LogoutFilter filter
This filter extends the Pac4J "LogoutFilter"; filter to trigger logout when closing the last Adelia Cloud session linked to the current Java EE session.
https://github.com/pac4j/jee-pac4j/wiki/Logout-configuration
The configuration of this filter is optional. It is used to trigger, via an HTTP request, the invalidation of the JEE session and, optionally, the OP session (in the case of Open ID, disconnect the user at identity provider level).
The filter declares an additional parameter in relation to the original:
- preventCloudLogout (set to "true" by default) prevents logout if several Cloud sessions are associated with the current JEE session (several browser tabs are open in the application). In this case, logout will only be carried out when the last cloud session is closed.
Filter declaration in web.xml:
<filter> <filter-name>LogoutFilter</filter-name> <filter-class>com.hardis.adelia.jee.security.filter.LogoutFilter</filter-class> <init-param> <param-name>centralLogout</param-name> <param-value>true</param-value> </init-param> ... </filter> ... <filter-mapping> <filter-name>LogoutFilter</filter-name> <url-pattern>/jee/logout</url-pattern> </filter-mapping> ...
Using with Adelia Cloud:
To automatically call the filter when the cloud session is closed by the user, simply specify the URL on which the filter is configured (/jee/logout in the example) in the configuration of the "logoutUrl"; parameter of the wagon desktop:
<desktopApplication name="default" ... logoutURL="jee/logout" ... />
Installation
The extension is not supplied with Adelia but can be accessed using continuous integration tools via the Hardis repository. Please refer to the "Build Adelia"; (application area-based build or component-based build) documentation for the repository configuration parameters.
You need to add the "adelia-jee-security" artifact from the "com.hardis.adelia"; group to your dependencies.
For example, in a Gradle template for Adelia Cloud, you need to add the following instruction to the build.gradle "dependencies" section:
runtime group:'com.hardis.adelia',name:'adelia-jee-security',version:'${project.ext.adeliaVersion}'
Important: the extension places the Pac4J basic modules as well as the OpenID extension in dependencies. If you want to use other security extensions, you need to reference them explicitly.
For example, you need "pac4j-ldap"; and "pac4j-http"; to use the LDAP module with form-based authentication. The Pac4J version currently used is 3.8.0:
runtime group:'org.pac4j',name:'pac4j-ldap',version:'3.8.0' runtime group:'org.pac4j',name:'pac4j-http',version:'3.8.0'