I have configured a LDAP realm in Glassfish, and authentication works just fine.
Now I am wondering how could I match the Principal.getName() return to a certain attribute of my LDAP user object. I thought it would use something such as "givenName" by default, but it returns the username used for authentication.
I don't mind making an extra trip to the LDAP server to obtain the additional information, but instead of keeping the LDAP connection attributes in my application, I'd like to inject the security realm (if such a thing is possible) and use its own connection.
So, in short, the questions are:
1) Can I map additional attributes to the Principal returned by the realm?
2) If number one is not possible, then how could I reuse the realm's information in order to connect to the LDAP server and obtain the data I need?
Thanks in advance for any help or suggestions.
The JAAS Subject often contains many principals, each one representing a different attribute.
For Java EE one, and only one, of these Principals is selected for the one that is returned when you call HttpServletRequest#getUserPrincipal and similar methods. The other Principals are for the Java EE API just lost.
You can determine which of those Principals to select by writing a JASPIC authentication module if the login happens via HTTP or SOAP.
You can preserve the entire Subject by putting it into the HTTP session from within the JASPIC authentication module. Other code can pick it up from there.
Edited: I was under the impression that the following used to work, at least with GlassFish 4.0. Unfortunately, that doesn't (any longer) seem to be the case. A workaround can be found in the comments of this issue.
Not really a solution per se; just a little detail I kept overlooking for a while, and which was quite a relief for me to have now become aware of. So --skipping the boring specifics-- I realized that a CallerPrincipalCallback(Subject s, Principal p) constructor is additionally available, which, when supplied with my custom Principal, causes the server to actually retain it, instead of wrapping it in or transforming it into an internal GlassFish implementation instance, as I previously thought it would. From "userspace" I was then able to access my "enriched" (more Subject- than Principal-like, to be honest) version the usual way (e.g. ExternalContext#getUserPrincipal, etc.), cast it and enjoy the convenience of not having to care about deriving custom Principals from generic ones in each application from now on :) .
Well, I could not extend the Principal attribute mapping without using a custom LoginModule; so, instead I opted to the solution described here: http://docs.oracle.com/cd/E19798-01/821-1751/abllk/index.html
What I do is, upon authentication, use the injected LDAP context to go back to the LDAP server and obtain the attributes I want. The downsides are obvious: two trips to the server instead of a single one, and the extra code to probe attributes and tie them to the Principal (or another POJO) in some way.
Related
When I try to access Liferay Portal session data it seems as it doesn't contain data stored by internal Liferay Portal processes. Is it possible to access the token that was stored in the OpenId-Connect Login process?
Basically, I was tasked with finding Software, that can make implementing a Portal, which displays functionality offered by API-Endpoints of multiple different internal Platforms easier. Currently, I'm looking at Liferay Portal 7.2. For the Login, I've used the OpenId-Connect implementation of Liferay Portal since authentication is handled by an internal Login server. The access token returned at the end of the OpenID-connect login process is an API-Token which I then want to use to access various API-Endpoints.
Currently I get the Session like this
HttpSession httpSession = PortalUtil.getHttpServletRequest(actionRequest).getSession();
After looking at the OpenId-Connect implementation in
com.liferay.portal.security.sso.openid.connect.internal.OpenIdConnectServiceHandlerImpl
I then tried to get the Session Object like this.
Object openIdConnectSessionObject = httpSession.getAttribute("OPEN_ID_CONNECT_SESSION");
But at this point, openIdConnectSessionObject is always null. I have read that different scopes have different sessions, but is there a way to access this data or is Liferay Portal maybe not really fit for what I'm trying to do.
There's one detail worth noting:
PortalUtil.getHttpServletRequest(actionRequest) will give you access to an artificial PortletRequest object that adheres to the HttpServletRequest interface, in case you need that for some API that expects requests from the servlet, not the portal, world.
A PortletRequest contains only parameters (and session information) directed to a particular portlet, and nothing else.
If you're interested in the actual underlying HttpServletRequest, you'll need PortalUtil.getOriginalServletRequest, which takes an HttpServletRequest as input. And you'll get that the way you've already explored. In the end, you'll have
session = PortalUtil.getOriginalServletRequest(
PortalUtil.getHttpServletRequest(actionRequest)).getSession();
to get the actual server's session.
I've not checked if that is useful, or even an advisable solution to the problem you state, but it might bring you further along solving your problem.
The issue
I'm working on an application, which, as many applications, starts with a login page before showing any kind of data.
The problem is that my client specifically requested that the credentials entered should be used to access the database.
This means that, instead of running the username / password against a "user" table, they will be used to acquire the user's database personal access.
It was decided by my superiors that this application would be build on top of a SpringBoot skeleton, which happens to be using a Stateless JWT Authentication mechanism.
I'm no expert when it comes to comparing the benefits of Stateless vs Stateful, but if I understood the concept correctly, this means that my application will need to re-establish the database connection with every single request, right?
I'm asking this because we are experiencing very slow response times.
The code seems to hang a little while on database setup related code, such as
dataSrc.getConnection();
or
entityManagerFactoryBean.afterPropertiesSet();
A possible solution?
I've heard of Hibernate's StatelessSession, but I was unsuccessful in setting it up.
I'm not even sure it would help at all, but from what I read, it uses a lower level Hibernate API, which might help mitigate the problem, without much of an impact on the way things are already coded since the SQL operations are exclusively stored procedure calls, which are manually mapped to Java objects.
What I need help with
Basically, I just want answers to 3 questions :
Question 1 :
Should I simply revert to Stateful authentication, because Stateless models are unadapted to my use case scenario?
Question 2 & 3 :
Can StatelessSession system even be implemented in my scenario, and would it make a significant difference on the database connection time?
But, of course, if you know of any other lead that my help me solve my problem without having to revert the whole thing to Stateful, I'm taking it!
Finally got time to answer this (in case someone passes by in the future).
Basically, I had two choices : remove Hibernate altogether or "go back" to Stateful sessions.
I went with the first option, wich I could do only because we had no annotation based mapping between our java objects and our database.
Going Stateful might have been a better approach, but i didn't really know how to do that. I found an impressive amount of articles underlining how to go Stateless, but not how to go back Stateful and... Well... Doing it backward wouldn't be enough, since I would be missing a lot of configuration, so I'd have to research it, and it was a hassle I had no time to deal with.
Using a custom implementation of org.springframework.web.filter.GenericFilterBean, I wrap every incoming request in a custom requestWrapper containing the database connection.
I open / create said connection using the java (low) API : java.sql.DriverManager.getConnection
I can then retreive this connection from my wrapper, wich is vehiculated through the application by Spring by using this code :
ServletRequestAttributes att = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
ContentCachingRequestWrapper springWrapper = (ContentCachingRequestWrapper) att.getRequest();
CustomWrapper myWrapper = (CustomWrapper) springWrapper.getRequest();
myWrapper.getConnection();
Just don't forget to properly close everything to avoid memory leak and you're set.
One would also need to register the driver properly, just by calling the constructor of said Driver in the application main class.
I've implemented a JAAS LoginModule that is working great with Spring 4 and Struts 2.3. This same LoginModule is also invoked via a ServletFilter in Tomcat 8.0/8.5 to authenticate and authorize requests to Servlets outside of the Spring framework.
The LoginModule uses a simple implementation of java.security.acl.Group and separates the User(s) and Role(s) with two simple implementations of java.security.Principal. By "simple" I mean the minimal implementations that satisfy the interfaces.
The "User" implementation maps the name property to a unique username (actually an e-mail address). Since the e-mail address is unique but could change, the account database contains a unique account identifier (GUID) that is used to assign groups, roles and to log service requests (while also anonymizing our users). In my model, the AccountIdentifier has its own class. Essentially, I have two unique identifiers for accounts, but since the e-mail address needs to be supplied to the LoginModule for authentication, it ended up being the basis for the User principal.
The account identifier is not currently being propagated through to the Subject in the LoginModule, but now I need it in order to log service requests.
I see two ways forward with making the account identifier available via the Subject, but I am uncertain which is the best practice for JAAS:
Extend my current "User" Principal implementation to include an "accountIdentifier" property that is set during LoginModule.commit().
Implement AccountIdentifier as a separate Principal that gets added to the Subject during the LoginModule.commit().
The first option would be easiest, but that also seems like it defeats the purpose of segregating Personally Identifying Information from accounts (which is something I need to do in order to satisfy the upcoming European GDPR requirements).
Should I even be adding the "User" principal (the one that contains the e-mail address) to the Subject?
There are several incompatibilities among the JAAS and Servlet specifications regarding authentication and user principles. Because of this, Spring uses a different approach to JAAS integration than Tomcat.
This answer documents a comprehensive method of implementing JAAS Login Modules in a way that accommodates both Tomcat and Spring.
For clarity, the two options for implementing user principles is copied here from the question:
Extend my current "User" Principal implementation to include an "accountIdentifier" property that is set during LoginModule.commit().
Implement AccountIdentifier as a separate Principal that gets added to the Subject during the LoginModule.commit().
Option 1 has the unfortunate side affect of joining together different forms of Personally Identifiable Information, which in some environments could violate the European GDPR regulations (sessions may be serialized onto disk, and this information would go with it).
Option 2 separates out Personally Identifiable Information, but must be implemented in a way that overcomes several limitations in the Servlet Specification and Tomcat's JAAS implementation.
These limitations are described in detail below, with the bold sections summarizing the main points.
JAASRealm requires that the backing collection(s) of a Subject preserve the ordering of the Principals.
The Tomcat 8.5 JAAS Realm documentation states:
Using JAASRealm gives the developer the ability to combine practically
any conceivable security realm with Tomcat's CMA.
but then goes on to state:
Although not specified in JAAS, you should create separate classes to
distinguish between users and roles, extending javax.security.Principal
so that Tomcat can tell which Principals returned from your login module
are users and which are roles (see org.apache.catalina.realm.JAASRealm).
Regardless, the first Principal returned is always treated as the user
Principal.
Note that the above Tomcat documentation uses the phrase "the user Principal". Although the JAAS API recommends implementing users and roles as distinct classes extending javax.security.Principal, this is not compatible with the Servlet Specification because HttpServletRequest.getUserPrincipal() allows only for a single Principal to be returned:
Returns a javax.security.Principal object containing the name of the
current authenticated user. If the user has not been authenticated,
the method returns null.
A strict reading of the above documentation states that it should contain "...the name of the current authenticated user", but in order to satisfy my original goal, I am interpreting this as "...any name or identifier for the authenticated Subject". This corresponds more closely to the com.sun.security.auth.UserPrincipal documentation (i.e., "A user principal identified by a username or account name").
Due to the above limitations in Tomcat's JAASRealm and the Servlet Specification's HttpServletRequest, it is clearly important that if the account identifier is to be propagated to the request via a ServletFilter (which only has access to the current session, request and response), it must be contained in the first Principal (thus Option 1 in the original question would satisfy this requirement, or Option 2 only if it appears first and I do not need the original username). I believe all I really need is the account identifier, so I am sticking with the second option for now, where I hand an "EmailAddressPrincipal" to MyLoginModule and I receive an "AccountIdentifierPrincipal" back via the Subject (i.e., the MyLogin.commit() adds the "AccountIdentifierPrincipal" as the very first principal).
The JAASRealm documentation is actually slightly contradictory regarding the precise order of Principals, it depends on which section you're reading:
As this Realm iterates over the Principals returned by
Subject.getPrincipals(), it will identify the first Principal that
matches the "user classes" list as the Principal for this user
vs.
Regardless, the first Principal returned is always treated as the user
Principal.
The Servlet API provides no guarantees of the ordering of Principles returned by a Subject.
Essentially, if I were to create a ServletFilter that mimics what JAASRealm is doing, the authentication would look like this (note the iterator in particular):
final LoginContext loginContext = new LoginContext(MyLoginModule.JAAS_REALM, new DefaultCallbackHandler(username, password));
loginContext.login();
final Subject subject = loginContext.getSubject();
request.getSession().setAttribute("AUTH_USER_PRINCIPAL", subject.getPrincipals(AccountIdentifierPrincipal.class).iterator().next());
request.getSession().setAttribute("AUTH_ROLE_PRINCIPALS", subject.getPrincipals(MyRolePrincipal.class));
Unfortunately, this is in direct conflict with the constructor for javax.security.auth.Subject, which mandates that a java.util.Set is used as the backing collection for Principals. Additionally, the Set.iterator() documentation states:
The elements are returned in no particular order (unless this set is
an instance of some class that provides a guarantee).
The earliest access we have to the Subject is in the LoginModule.initialize() method, which is something that is unfortunately invoked somewhere in the internals of LoginContext (I think). This means we have no control over the exact subclass of Set that is used as the backing collection for Principals, and therefore no control over their ordering. By the time this arrives to the ServletFilter, it is a SynchronizedSet, so it isn't even clear what the original class was, or whether re-ordering occurred.
This all indicates that in order for JAASRealm to work as expected only a single user principal can be provided. There is no interface anywhere in that middle layer that clearly establishes the order of the Subject Principals.
Conclusions
When using JAASRealm, only one Principal of the declared User type should be added to the Subject during commit.
When using JAASRealm, avoid using multiple User class names.
Violating the above two rules may lead to undefined and/or inconsistent behavior.
Solution: Use an AuthorityGranter for Spring, and a ServletFilter for non-framework Tomcat servlets
For the sake of Option 2, I'm avoiding the use of JAASRealm because according to all of the above documentation, it does not faithfully adhere to JAAS. This brings me back to the pure ServletFilter approach.
The javax.security.auth.Subject contains everything needed for authorization: multiple user principals, roles and ACL groups. Unfortunately, this class is only partially serializable, which means I can't just wrap the class as a Principal and return it.
In order to satisfy Spring's DefaultJaasAuthenticationProvider, implement an AuthorityGranter to map Principals to role names - that provides complete control over how the mapping is performed.
Since AuthorityGranter isn't available outside the Spring Framework, I also implemented a ServletFilter that uses a similar approach to map roles for my non-Spring webapp. Temporarily, I am using an HttpServletRequestWrapper to read the Principal and Roles from session attributes (stored in session during authentication) and override getUserPrincipal and isUserInRole. Ultimately, I will revisit JAASRealm to see if it contains any functionality for handling this piece, but I'm not quite there yet.
I have a RESTful service which uses the internal business service to save and fetch information from the databases. The webservice uri looks something like, /api/entity/{id}, to which the web UI application requests with the ID of the entity. Here the problem is the {id} can be literally anyone's id, that is, the record of someone else that should not be seeing by the user using the application. In order to solve this, using the Spring Security, I wrote a SPEL, something like,
#Service
interface EntityService {
#PostAuthorize("returnObject.userId==principal.id")
public ReturnObject get(long i);
}
Is the above approach the right way to solve this? (In my earlier projects my developers used to write all these security stuffs into their business methods.) As the complexity of the security grows for an entity, say, the administrator of the user (who have created the record) can see the object but not other administrators, or group administrators can see the record etc, this approach too gets complicated.
Besides, after digging my head into many of the Spring-ACL library classes, I somehow have managed to configure the Spring-ACL, invoke its permission evaluator evaluate the hasPermission method in the SPEL with the authorization of the entity and its unique-identifier loaded from the database.
#Service
interface EntityService {
#PreAuthorize("hasPermission(#id, 'com.project.domain.ReturnObject', 'read')")
public ReturnObject get(long id);
}
But the problem I see with this approach is, every time a user creates a record shouldn't the application create an ACL permission too (ACL_ENTRY) for that object? Is this a right way to approach this problem, or is there some other way I didn't see through? I know this is not a new problem, and someone out there should have solved it already in many ways. I want to know how the problem was solved, not in the traditional way, in the service logic or in the queries, but using frameworks such as Spring-ACL or Apache Shiro.
I can't answer if Shiro uses a different approach, but as Neil commented earlier,
Spring Security does not provide any special integration to automatically create, update or delete ACLs as part of your DAO or repository operations. Instead, you will need to write code [...] for your individual domain objects
— Spring Security Reference: Domain Object-based ACLs
The open-source OACC security framework (disclosure: I'm maintainer and co-author), on the other hand has the concept of create-permissions, which let you define exactly what permissions a user would get on an object after creating it.
OACC is an API designed to help you manage and query your permissions and security topology programmatically.
It supports fine-grained permissions on your individual domain objects, lets you model coarse-grained roles, and with its concepts of domains, permission inheritance and impersonation, can handle pretty much anything in between.
My specific problem is that when I attempt to bind with the following full dn, all is well
new LDAPConnection().bind(LDAPConnection.LDAP_V3,
"uid=me#wherever.com,ou=Lev1,ou=Lev2,o=Company", "secret".getBytes());
however, when I attempt to bind with an incomplete dn, I am getting an Invalid Credentials exception.
new LDAPConnection().bind(LDAPConnection.LDAP_V3,
"uid=me#wherever.com,ou=Lev1,o=Company", "secret".getBytes());
Is their some form of wildcarding that is possible, such as "uid=me#wherever.com,ou=Lev1,ou=*,o=Company"?
No. It sounds like you might be confusing bind() with search(). bind() is authentication against the directory, like logging in. Binding with a wildcarded dn would be like having a login with a wildcarded username. Doesn't really make sense.
To do a wildcard like this you have to do a search first and select which one you want to perform the bind. This may mean you need to bind with an id which can perform the search. ;)
No, the use of wildcards is not possible in the way you describe. However, SASL provides a mechanism to accomplish the desired behavior. Your directory server administrator may be able to configure the directory server to map identities to an authorization ID wherein the client need not know the distinguished name of the identity with which to authenticate the LDAP connection. Professional-quality directory server software will provide a variety of identity mapping capabilities.