0
votes

I am using CAS authentication for my application. I store my user details in LDAP Active Directory. I use spring security and JSF in my web application. I find difficulty in getting the LDAP attributes like country, country code to the managed bean. I am able to retrieve the roles, username, password from SecurityContext but I am not able to get the country details of the logged in user from the ldap.

I beleive there must be a way after the CAS authentication, I can retrieve the ldap attributes to JSF managed bean through CAS. I tried the below link but i couldnt get the details to the managed bean.

My CAS does get roles from LDAP, but I don't want my web app to talk to LDAP.

Can spring security + CAS be configured to get the ldap attributes ?

can someone help me in getting the ldap attributes like country after the CAS authentication ?

Get LDAP user attributes from CAS

I have attached my CAS deployerConfigContext.xml

            <?xml version="1.0" encoding="UTF-8"?>



            <beans xmlns="http://www.springframework.org/schema/beans"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:p="http://www.springframework.org/schema/p"
                   xmlns:c="http://www.springframework.org/schema/c"
                   xmlns:tx="http://www.springframework.org/schema/tx"
                   xmlns:util="http://www.springframework.org/schema/util"
                   xmlns:sec="http://www.springframework.org/schema/security"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
                   http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
                   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">



                 <bean id="authenticationManager" class="xxx.cas.authentication.XXXOTPPolicyBasedAuthenticationManager">

                    <constructor-arg index="0">
                        <list value-type="org.jasig.cas.authentication.AuthenticationHandler" >
                            <ref local="ldapAuthenticationHandler"/>
                            <ref local="radiusAuthenticationHandler"/>
                        </list>
                    </constructor-arg> 


                    <property name="authenticationPolicy">
                        <bean class="xxx.cas.authentication.XXXAllAuthenticationPolicy" />
                    </property>
                </bean>

                <!-- Required for proxy ticket mechanism. -->
                <bean id="proxyAuthenticationHandler"
                      class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
                      p:httpClient-ref="httpClient" p:requireSecure="false" />



                   <!--
                   | Change principalIdAttribute to use another directory attribute,
                   | e.g. userPrincipalName, for the NetID
                   -->
                <bean id="ldapAuthenticationHandler"
                      class="org.jasig.cas.authentication.LdapAuthenticationHandler"
                      p:principalIdAttribute="sAMAccountName"
                      c:authenticator-ref="authenticator">
                    <property name="principalAttributeMap">
                        <map>
                            <!--
                               | This map provides a simple attribute resolution mechanism.
                               | Keys are LDAP attribute names, values are CAS attribute names.
                               | Use this facility instead of a PrincipalResolver if LDAP is
                               | the only attribute source
                               -->
                            <entry key="displayName" value="displayName" />
                            <entry key="facsimileTelephoneNumber" value="facsimileTelephoneNumber" />
                            <entry key="memberOf" value="memberOf" />
                            <entry key="co" value="co" />
                            <entry key="c" value="c" />
                            <entry key="mail" value="mail" />
                            <entry key="description" value="role" />
                        </map>
                    </property>
                </bean>



                <!-- Required for proxy ticket mechanism -->
                <bean id="proxyPrincipalResolver"
                      class="org.jasig.cas.authentication.principal.BasicPrincipalResolver" />


                <!-- Radius authentication -->
                <bean id="radiusClientFactory"
                      class="org.jasig.cas.adaptors.radius.RadiusClientFactory"
                      p:inetAddress="${fourtress.server}"
                      p:sharedSecret="${fourtress.ss}" />

                <bean id="radiusServer"
                      class="org.jasig.cas.adaptors.radius.JRadiusServerImpl"
                      c:protocol="PAP"
                      c:clientFactory-ref="radiusClientFactory" />

                <bean id="radiusAuthenticationHandler"
                      class="xxx.cas.authentication.XXXRadiusAuthenticationHandler">
                  <property name="servers">
                      <list>
                          <ref local="radiusServer" />
                      </list>
                  </property>
                </bean>




                <bean id="attributeRepository"
                  class="org.jasig.cas.persondir.LdapPersonAttributeDao"
                  p:connectionFactory-ref="pooledLdapConnectionFactory"
                  p:baseDN="ou=users,ou=egate,dc=egate-t,dc=local" p:searchControls-ref="searchControls" p:searchFilter="sAMAccountName={0}">

                    <property name="requireAllQueryAttributes" value="true"/>
                    <property name="queryAttributeMapping">
                        <map>
                            <!-- Attribute mapping between principal (key) and LDAP (value) names used to perform the LDAP search -->
                            <entry key="username" value="sAMAccountName" />
                        </map>
                    </property>
                    <property name="resultAttributeMapping">
                        <map>
                            <!-- Mapping between LDAP entry attributes (key) and Principal's (value) -->
                            <entry key="memberOf" value="memberOf" />
                            <entry key="mail" value="mail" />
                            <entry key="cn" value="FullName" />
                            <entry key="sn" value="LastName" />
                            <entry key="displayName" value="displayName" />
                            <entry key="description" value="role" />
                            <entry key="facsimileTelephoneNumber" value="facsimileTelephoneNumber" />
                            <entry key="co" value="country" />
                            <entry key="c" value="countryCode" />

                        </map>
                    </property>
                </bean>

                <bean id="searchControls"
                      class="javax.naming.directory.SearchControls"
                      p:searchScope="2"
                      p:countLimit="0" p:timeLimit="0" />


                <!--
                Sample, in-memory data store for the ServiceRegistry. A real implementation
                would probably want to replace this with the JPA-backed ServiceRegistry DAO
                The name of this bean should remain "serviceRegistryDao".
                +-->
                <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"
                        p:registeredServices-ref="registeredServicesList" />

                <util:list id="registeredServicesList">

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="1" />
                        <property name="name" value="cassimple" />
                        <property name="description" value="cassimple application 1" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cassimple/.*" />
                        <property name="evaluationOrder" value="10000001" />
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="2" />
                        <property name="name" value="casldap" />
                        <property name="description" value="casldap application 2" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/casldap/.*" />
                        <property name="evaluationOrder" value="10000002" />
                        <property name="allowedAttributes">
                            <list>
                                <value>memberOf</value>
                                <value>LastName</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>role</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="3" />
                        <property name="name" value="cascir" />
                        <property name="description" value="cas cir application 3" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cascir/.*" />
                        <property name="evaluationOrder" value="10000003" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="4" />
                        <property name="name" value="egate" />
                        <property name="description" value="cas egate application 4" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/egate/.*" />
                        <property name="evaluationOrder" value="10000004" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="5" />
                        <property name="name" value="cir" />
                        <property name="description" value="cas cir application 5" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cir/.*" />
                        <property name="evaluationOrder" value="10000005" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                                <value>country</value>
                                <value>countryCode</value>
                            </list>
                        </property>            
                    </bean>





                </util:list>

                <bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" />

                <bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor" p:monitors-ref="monitorsList" />

                <util:list id="monitorsList">
                  <bean class="org.jasig.cas.monitor.MemoryMonitor" p:freeMemoryWarnThreshold="10" />
                  <!--
                    NOTE
                    The following ticket registries support SessionMonitor:
                      * DefaultTicketRegistry
                      * JpaTicketRegistry
                    Remove this monitor if you use an unsupported registry.
                  -->
                  <bean class="org.jasig.cas.monitor.SessionMonitor"
                      p:ticketRegistry-ref="ticketRegistry"
                      p:serviceTicketCountWarnThreshold="5000"
                      p:sessionCountWarnThreshold="100000" />
                </util:list>


                <bean id="authenticator" class="org.ldaptive.auth.Authenticator"
                      c:resolver-ref="dnResolver"
                      c:handler-ref="authHandler"
                      p:entryResolver-ref="entryResolver" />

                <!-- Active Directory UPN format. -->
                <bean id="dnResolver"
                      class="org.ldaptive.auth.FormatDnResolver"
                      c:format="${ldap.authn.format}" />

                <bean id="authHandler" class="org.ldaptive.auth.PooledBindAuthenticationHandler"
                      p:connectionFactory-ref="pooledLdapConnectionFactory" />

                <bean id="pooledLdapConnectionFactory" 
                      class="org.ldaptive.pool.PooledConnectionFactory"
                      p:connectionPool-ref="connectionPool" />


                <bean id="connectionPool"
                      class="org.ldaptive.pool.BlockingConnectionPool"
                      init-method="initialize"
                      p:poolConfig-ref="ldapPoolConfig"
                      p:blockWaitTime="${ldap.pool.blockWaitTime}"
                      p:validator-ref="searchValidator"
                      p:pruneStrategy-ref="pruneStrategy"
                      p:connectionFactory-ref="connectionFactory" />

                <bean id="ldapPoolConfig" class="org.ldaptive.pool.PoolConfig"
                      p:minPoolSize="${ldap.pool.minSize}"
                      p:maxPoolSize="${ldap.pool.maxSize}"
                      p:validateOnCheckOut="${ldap.pool.validateOnCheckout}"
                      p:validatePeriodically="${ldap.pool.validatePeriodically}"
                      p:validatePeriod="${ldap.pool.validatePeriod}" />

                <bean id="connectionFactory" class="org.ldaptive.DefaultConnectionFactory"
                      p:connectionConfig-ref="connectionConfig" />

                <bean id="connectionConfig" class="org.ldaptive.ConnectionConfig"
                      p:ldapUrl="${ldap.url}"
                      p:connectTimeout="${ldap.connectTimeout}"
                      p:useStartTLS="${ldap.useStartTLS}"
                      p:sslConfig-ref="sslConfig"/>

                <bean id="sslConfig" class="org.ldaptive.ssl.SslConfig">
                    <property name="credentialConfig">
                        <bean class="org.ldaptive.ssl.X509CredentialConfig"
                              p:trustCertificates="classpath:${ldap.trustedCert}" />
                    </property>
                </bean>

                <bean id="pruneStrategy" class="org.ldaptive.pool.IdlePruneStrategy"
                      p:prunePeriod="${ldap.pool.prunePeriod}"
                      p:idleTime="${ldap.pool.idleTime}" />

                <bean id="searchValidator" class="org.ldaptive.pool.SearchValidator" />

                <bean id="entryResolver"
                      class="org.jasig.cas.authentication.support.UpnSearchEntryResolver"
                      p:baseDn="${ldap.baseDn}" />


            </beans>

My spring security.xml

        <beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

            <debug />


            <global-method-security secured-annotations="enabled" />

            <http auto-config="false" use-expressions="true"
                entry-point-ref="casAuthenticationEntryPoint">

                <custom-filter position="CAS_FILTER" ref="casAuthenticationFilter" />


                <intercept-url pattern="/faces/disclaimer**" access="permitAll" />
                <intercept-url pattern="/faces/searchCreditInstitution**"
                    access="permitAll" />
                <intercept-url pattern="/faces/searchParentInstitution**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/createCreditInstitution**"
                    access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/authorisation**"
                    access="hasAuthority('ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/rejected**" access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/pendingApproval**"
                    access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/auditLog**"
                    access="hasAuthority('ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/enquiry**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER','ROLE_CIR_XXXOPS')" />
                <intercept-url pattern="/faces/changePassword**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER','ROLE_CIR_XXXOPS')" />
                <intercept-url pattern="/faces/dashboard**" access="ROLE_CIR_XXXOPS" />
                <intercept-url pattern="/resources**" access="permitAll" />
                <intercept-url pattern="/faces/javax.faces.resource**" access="permitAll" />
                <logout logout-url="/logout" logout-success-url="https://localhost:7002/cas/logout" />

            </http>

            <beans:bean id="serviceProperties"
                class="org.springframework.security.cas.ServiceProperties">
                <beans:property name="service"
                    value="https://localhost:7002/cir/j_spring_cas_security_check" />
                <beans:property name="sendRenew" value="false" />
            </beans:bean>

            <beans:bean id="casAuthenticationEntryPoint"
                class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
                <beans:property name="loginUrl" value="https://localhost:7002/cas/login" />
                <beans:property name="serviceProperties" ref="serviceProperties" />
            </beans:bean>

            <beans:bean id="casAuthenticationFilter"
                class="org.springframework.security.cas.web.CasAuthenticationFilter">
                <beans:property name="authenticationManager" ref="casAuthenticationManager" />
            </beans:bean>

            <authentication-manager alias="casAuthenticationManager">
                <authentication-provider ref="casAuthenticationProvider" />
            </authentication-manager>

            <beans:bean id="casAuthenticationProvider"
                class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
                <beans:property name="serviceProperties" ref="serviceProperties" />
                <beans:property name="ticketValidator" ref="ticketValidator" />
                <beans:property name="authenticationUserDetailsService"
                    ref="authenticationUserDetailsService" />
                <beans:property name="key" value="cir" />
            </beans:bean>

            <beans:bean id="ticketValidator"
                class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0"
                    value="https://localhost:7002/cas">
                </beans:constructor-arg>
            </beans:bean>

            <beans:bean id="authenticationUserDetailsService"
                class="XXX.cir.cas.authentication.XXXCasAuthenticationUserDetailsService" />

        </beans:beans>

I am able to get the Ldap roles, username in JSF managed bean by this way but not country

                SecurityContext ctx = SecurityContextHolder.getContext();

                UserDetails userDetails = (UserDetails)ctx.getAuthentication().getPrincipal();      

                System.out.println("Role of the ldaper : " + ctx.getAuthentication().getAuthorities());     

                String userRole = ctx.getAuthentication().getAuthorities(); 
1

1 Answers

1
votes

I haven't used Spring Security but one option could be AttributeRepository in order to get attributes directly from cas instead of retrieving from your application. You can add an attributeRepository to your deployerContext (bean authenticationManager, property credentialsToPrincipalResolvers:

 <bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" 
                p:attributeRepository-ref="attributeRepository"/>

And the following beans:

<bean id="attributeRepository" parent="baseAttributeRepository">
    <property name="personAttributeDaos">
        <list>
            <ref local="ldapAttributesByUid" />
        </list>
    </property>
</bean>  
<bean id="ldapAttributesByUid" parent="baseLdapAttributeRepository"
      class="org.jasig.services.persondir.support.ldap.LdapPersonAttributeDao">
    <property name="queryAttributeMapping">
        <map>
            <entry key="username" value="uid" /> 
        </map>
    </property>
</bean>    
<bean id="baseLdapAttributeRepository" abstract="true"
      p:contextSource-ref="contextSource"
      p:baseDN="o=xxxxxx,dc=xxxxx"
      p:requireAllQueryAttributes="true">
    <property name="resultAttributeMapping">
        <map>
            <entry key="accountState" value="accountState" /> 
            <entry key="authId">
                <list>
                    <value>authId</value>
                    <value>Formatted Name</value>
                </list>
            </entry>
            <entry key="groupMembership" value="groupMembership" />
            <entry key="uid" value="uid" />
            <entry key="sn" value="sn" />
            <entry key="sn2" value="sn2" />
            <entry key="givenName" value="givenName" />
        </map>
    </property>
</bean>  

AFAIK, bear in mind that SAML must be used instead of CAS Protocol. More info at https://wiki.jasig.org/display/CASUM/Attributes

Hope this helps