In my application I use form-based authentication with a LDAP-Realm. For Authorization I use a database. As I understand this works as follows
App --> (user, pass) --> LDAP
<-- OK, user exists --
--> ask for security roles for 'user' --> JACC / Database
<-- Administrator --
Can I hook into the process where my application calls ask for security roles for 'user'
?
Background:
LDAP says: Okay, 'user' is authentified
Database : give me all roles where username = user
And now I want to customize the Database query: give me all roles where username = 'user' AND some more attributes
Is this somehow possible?
TL;DR: Have a look at this and that for a sample solution.
What you ask for depends on the degree of flexibility offered by the vendor-specific feature you use; your product may or may not allow you to extend the behaviour of that/these
LoginModule
/Realm
/IdentityStore
/ whatever-it's/they're-called proprietary class(es), or maybe even to just type an SQL query into some administrative UI's input field. The bottom line is that it's non-standard functionality.On the standard Java EE side of the spectrum there are the JASPIC (user / message authentication) and JACC (authorization) SPIs. Both can be used to retrieve security-related information pertaining to your users from some external store. What JASPIC cannot do is change a user's roles after authentication; that is, for the duration of an authenticated request 1 the user's roles are fixed. JASPIC can also not attach meaning to those roles; for it they're just plain
String
s that the AS will in some proprietary manner derive groupPrincipal
s from. JACC, on the other hand can do those things, as it establishes a "rulebase" (thinkPolicy
) which precisely associates roles, principals andPermission
s and can be queried on each and every user-system interaction. JACC can also override or alter the interpretation of the Java EE security constraints expressed via deployment descriptors and annotations.I will include a JASPIC-based solution in this post and disregard JACC for the most part, because:
Some remarks on what follows:
LoginModule
(LM), by employing the JASPIC LoginModule Bridge Profile. The latter SAM requires further configuration / adaptation, both at the AS and at the source level per se, except if you are using GlassFish. An accompanying example JAASlogin.conf
entry is provided.AuthConfigProvider
andServerAuthConfig
implementations, but will then have to register the actual SAM with your product'sAuthConfigFactory
in a proprietary way (via a vendor-specificfoo-web.xml
and/or further use of deployment / administrative tools). You will additionally not have the SAM implementing theServerAuthContext
interface and will have to load the accompanyingProperties
from within the SAM. Your AS will then instantiate the missing classes for you, possibly reusing a "global"AuthConfigProvider
and/orServerAuthConfig
it has pre-configured for all applications and message layers. Note that, depending on whether it reuses its instantiatedServerAuthConfig
andServerAuthContext
across requests (hardly ever the case especially with the latter), the lifecycle of your SAM may be affected.provided
)javaee-api
7.0 (plus your JDBC driver, unless already present on the AS class path).ServletContextListener
registering theAuthConfigProvider
. Save as/<project>/src/main/java/org/my/foosoft/authn/BigJaspicFactoryRegistrar.java
.AuthConfigProvider
. Save as/<project>/src/main/java/org/my/foosoft/authn/BigJaspicFactory.java
.ServerAuthConfig
. Save as/<project>/src/main/java/org/my/foosoft/authn/LittleJaspicServerFactory.java
.ServerAuthContext
-ServerAuthModule
implementation helper class. This is part 1/3 of the actual answer. Save as/<project>/src/main/java/org/my/foosoft/authn/HttpServletSam.java
./<project>/src/main/java/org/my/foosoft/authn/StandaloneLdapSam.java
./<project>/src/main/java/org/my/foosoft/authn/JaasDelegatingLdapSam.java
./<project>/src/main/java/org/my/foosoft/authn/JaspicMischief.java
.Properties
. Adapt them to your needs if you'd like to test the actual authentication code verbatim. Save as/<project>/src/main/resources/org/my/foosoft/authn/jaspic-provider.properties
.login.conf
snippet for (6); consult vendor's documentation for actual file system location./<project>/src/main/java/org/my/foosoft/presentation/UserUtils.java
(optional - for demonstration purposes: JSF backing bean)/<project>/src/main/webapp/index.xhtml
(optional - for demonstration purposes: unprotected index page)/<project>/src/main/webapp/login.xhtml
(optional - for demonstration purposes: login page)/<project>/src/main/webapp/restricted/info.xhtml
(optional - for demonstration purposes: protected index page for users in roleaccess_restricted_pages
)/<project>/src/main/webapp/WEB-INF/web.xml
(optional - for demonstration purposes and for the sake of completness: web module DD)Further reading:
1 JASPIC is very a generic SPI, in theory able to authenticate JMS, SAML-over-SOAP and any other kind of message, when plugged into a capable message processing runtime. Even its predominantly used Servlet Container Profile does not overly constrain it.
JASPIC's low-level, flexible nature entails unawareness of protocol-specific functionality, such as the HTTP session. Consequently,
ServerAuthContext
s / SAMs are triggered by the runtime to perform authentication on every request.The spec however makes a provision about this potential shortcoming by allowing SAMs to request initiation of a container authentication session by the runtime, via a
MessageInfo
Callback Property. When asked to authenticate subsequent requests of the same client, SAMs can avoid repeating the entire process, by asking the runtime to reuse the previously established AS authentication session, hence the user identity (caller and/or groupPrincipal
(s)). That is accomplished via execution of the "do-nothing-/leave-authentication-state-as-is-protocol" shown in theHttpServletSam
of the sample code.It should lastly be noted that neither the JASPIC nor the Servlet spec clearly defines what a container authentication session is. For a SAM-authenticated user I would, for all practical purposes, consider the AS authentication session to be the equivalent of the HTTP session, as long as a) authentication pertains to a single application context and b) the SAM, as explained above, signals reuse of the AS authentication session on each request.
You could customize it using Spring Security, authenticating against LDAP and configuring the authentication manager/authentication-provider: