0

I've got a Java doLogin() method invoked from a JSF page that gets an id (String netId) and password (String password) from the user. doLogin() initiates the authentication using netId as the principal in an Active Directory login. After that, I would like to get other attributes besides principal name from the Directory that secures my app.

My security is configured in the container & it works, such that

HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();

req.login(netID, password);

is successful and

req.getUserPrincipal().getName();

returns the user's netID. However, my app uses the netId only for authentication. Other attributes (commonName for example) are needed for other parts of the app that access another database. I want to do something like

usefulLDAPobj = *getLDAPSession from "somewhere" in the HTTP Session, the FacesContext or some other available object*

String cn = usefulLDAPobj.getAttributeFromProfile ("cn");

ses.setAttribute("username", cn);

and from then on use username, stored in the session, in my Hibernate ORM.

I know the simple-minded usefulLDAPobj.getAttributeFromProfile ("cn") will be more complex, but I can fill that out if I can find a starting point that gets me access to the LDAP Directory.

Since there is an obvious LDAP connection being set up by the container I feel there must be a way for me to make use of it without having to manually build up an LdapContext programatically; which would require the code to know all the LDAP server / bind-DN / bind-password configuration that the web server (JBoss EAP 6.2) already knows about (from the <login-module> defined in standalone.xml). For example, methods like getUserPrincipal() and isUserInRole() need access to the very same Directory profile that I want access to.

So my question is: is there a way to get an LDAP connection or context from a FacesContext or a HTTPServletRequest or any objects accessible from an HTTPServlet?

PMorganCA
  • 730
  • 6
  • 24

2 Answers2

0

What is the easiest way to get an LdapConext from a FacesConext?

There isn't a way at all, let alone an easy way. JSF doesn't assume the presence of an LDAP server, and doesn't provide any LDAP-related APIs.

Since there is an obvious LDAP connection being set up by the container

When you logged in. Not permanently. If there was an LDAP server at all. And JSF doesn't know how the container logged you in.

I feel there must be a way ...

There isn't.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • @EJB - perhaps I should clarify... at the time I call req.login() there is a connection to the Directory. That's when I would like to query for more attributes. – PMorganCA Apr 06 '15 at 23:21
  • Doesn't change my answer. If you need more information about the logged-in user you will have to look it up yourself, or else write a container-specific login module that stores the extra information you need in the user principal. – user207421 Apr 07 '15 at 09:25
0

I think a useful answer to the question would be there's no way to get an LDAPContext directly from FacesContext, but by writing a container-specific Login Module and Principal class you can pass additional data via the HttpServletRequest that is obtained through FacesContext.

I'll put the details of my solution here, because even though it's not directly related to FacesContext it gives me what I asked for in the body of the question, which is a way to get additional user data from the LDAP profile while avoiding the need to create a whole separate LDAPContext.

What I wanted specifically was the CN, which I was able to parse out of the DN without doing an additional search. If I needed any other data, I assume that I could get that using ctx in findUserDN() below.

I guess I am making my app dependent on JBoss with this solution, and if that were undesirable I would search for a JBoss-independent login module class to extend (no idea if that would be easy, difficult or impossible).

Here's my solution:

  1. Override findUserDN (LdapContext ctx) in AdvancedADLoginModule

    package ca.mycompany.myapp.jboss;
    
    import java.security.Principal;
    
    import javax.naming.ldap.LdapContext;
    import javax.security.auth.login.LoginException;
    
    import org.jboss.security.negotiation.AdvancedADLoginModule;
    
    public class NameFetchingADLoginModule extends AdvancedADLoginModule
    
        @Override
        protected String findUserDN(LdapContext ctx) throws LoginException
        {
            String lclUserDN = super.findUserDN(ctx);
    
            Principal principal = getIdentity();
    
            if (principal instanceof PrincipalWithDisplayName)
            {
                String displayName = lclUserDN.substring(3, lclUserDN.indexOf(','));
                ((PrincipalWithDisplayName) principal).setDisplayName (displayName);
            }
    
            return lclUserDN;
        }
    }
    
  2. extend Principal to provide a displayName attribute

    package ca.mycompany.myapp.jboss;
    
    import java.io.Serializable;
    import java.security.Principal;
    
    public class PrincipalWithDisplayName implements Serializable, Principal
    {
        private static final long serialVersionUID = 1L;
        private final String name;
    
        // additional attribute provided by this subclass
        private String displayName;
    
        public PrincipalWithDisplayName(final String name) {
            this.name = name;
        }
    
        // new and overriding getters and setters, equals() and hashCode() removed for brevity
    }
    
  3. use the new login module and Principal in a doLogin() method

snippet:

    String displayName = "";
    HttpSession ses = FacesContext.getCurrentInstance().getExternalContext().getSession (false);
    HttpServletRequest req = FacesContext.getCurrentInstance().getExternalContext().getRequest();

    try {           
        req.login(userName, password); // this throws an exception if authentication fails

        Principal lclUser = req.getUserPrincipal();
        if (lclUser instanceof PrincipalWithDisplayName)
        {
            displayName = ((PrincipalWithDisplayName) lclUser).getDisplayName ();
        }

        // get Http Session and store username
        //
        HttpSession session = HttpUtil.getSession();
        sess.setAttribute("username", displayName);
        ...
  1. Configure JBoss EAP 6.2, in standalone.xml, to use the new classes

snippet:

<subsystem xmlns="urn:jboss:domain:security:1.2">
    <security-domains>
        <security-domain name="company_ad" cache-type="default">
            <authentication>
                <login-module code="ca.mycompany.myapp.jboss.NameFetchingADLoginModule" flag="required">
                    <module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
                    <module-option name="java.naming.provider.url" value="ldap://servernm.mycompany.tst:389"/>
                    <module-option name="java.naming.security.authentication" value="simple"/>
                    <module-option name="bindDN" value="CN=AuthGuy,OU=Accounts,OU=Company User Accounts,DC=company,DC=tst"/>
                    <module-option name="bindCredential" value="Snowden1"/>
                    <module-option name="baseCtxDN" value="OU=Company User Accounts,DC=company,DC=tst"/>
                    <module-option name="baseFilter" value="(sAMnetID={0})"/>
                    <module-option name="searchScope" value="SUBTREE_SCOPE"/>
                    <module-option name="allowEmptyPassword" value="false"/>
                    <module-option name="rolesCtxDN" value="OU=Company User Accounts,DC=company,DC=tst"/>
                    <module-option name="roleFilter" value="(sAMAccountName={0})"/>
                    <module-option name="roleAttributeID" value="memberOf"/>
                    <module-option name="roleAttributeIsDN" value="true"/>
                    <module-option name="roleNameAttributeID" value="cn"/>
                    <module-option name="recurseRoles" value="1"/>
                    <module-option name="principalClass" value="ca.mycompany.myapp.jboss.PrincipalWithDisplayName"/>
                </login-module>
            </authentication>
        </security-domain>
    </security-domains>
</subsystem>
PMorganCA
  • 730
  • 6
  • 24