This describes how to authenticate against Active Directory with multiple OU's with Shibboleth version 3.2.1, specifically 1 AD server (or 2 mirrored servers) with 4 OU's. The LDAP documentation suggests to connect to multiple OU's you can use idp.authn.LDAP.userFilter=(&(|(ou:dn:=people)(ou:dn:=guests))(uid={user})) but unfortunately thats not supported by Active Directory.
Step-by-step guide
Add another authenticator to ldap-authn-config.xml
Code Block language xml theme Confluence title ldap-authn-config.xml collapse true <!-- Active Directory Configuration for 1 server with 4 OU's --> <bean id="adAuthenticator" class="org.ldaptive.auth.Authenticator" p:authenticationResponseHandlers-ref="authenticationResponseHandler" p:resolveEntryOnFailure="%{idp.authn.LDAP.resolveEntryOnFailure:false}"> <constructor-arg index="0" ref="formatDnResolver" /> <constructor-arg index="1" ref="authHandler" /> </bean> <bean id="authenticationResponseHandler" class="org.ldaptive.auth.ext.ActiveDirectoryAuthenticationResponseHandler" /> <!-- Define Directory1 connection pool --> <bean id="adConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" abstract="true" p:blockWaitTime="%{idp.pool.LDAP.blockWaitTime:3000}" p:poolConfig-ref="adPoolConfig" p:pruneStrategy-ref="adPruneStrategy" p:validator-ref="adSearchValidator" p:failFastInitialize="%{idp.pool.LDAP.failFastInitialize:false}" /> <bean id="adPoolConfig" class="org.ldaptive.pool.PoolConfig" p:minPoolSize="%{idp.pool.LDAP.minSize:3}" p:maxPoolSize="%{idp.pool.LDAP.maxSize:10}" p:validateOnCheckOut="%{idp.pool.LDAP.validateOnCheckout:false}" p:validatePeriodically="%{idp.pool.LDAP.validatePeriodically:true}" p:validatePeriod="%{idp.pool.LDAP.validatePeriod:300}" /> <bean id="adPruneStrategy" class="org.ldaptive.pool.IdlePruneStrategy" p:prunePeriod="%{idp.pool.LDAP.prunePeriod:300}" p:idleTime="%{idp.pool.LDAP.idleTime:600}" /> <bean id="adSearchValidator" class="org.ldaptive.pool.SearchValidator" /> <!-- Directory1 connection pool settings --> <bean id="adConnectionConfig" class="org.ldaptive.ConnectionConfig" abstract="true" p:ldapUrl="%{idp.authn.LDAP.ldapURL}" p:useStartTLS="%{idp.authn.LDAP.useStartTLS:true}" p:useSSL="%{idp.authn.LDAP.useSSL:false}" p:connectTimeout="%{idp.authn.LDAP.connectTimeout:3000}" p:sslConfig-ref="adSslConfig" /> <alias name="%{idp.authn.LDAP.sslConfig:certificateTrust}" alias="adSslConfig" /> <!-- ldap.properties "idp.authn.LDAP.authenticator = adAggregateAuthenticator" --> <bean id="adAggregateAuthenticator" class="org.ldaptive.auth.Authenticator" p:authenticationResponseHandlers-ref="adAuthenticationResponseHandler"> <constructor-arg index="0" ref="adAggregateDnResolver" /> <constructor-arg index="1" ref="adAggregateAuthHandler" /> </bean> <bean id="adAuthenticationResponseHandler" class="org.ldaptive.auth.ext.ActiveDirectoryAuthenticationResponseHandler" /> <bean id="adAggregateDnResolver" class="org.ldaptive.auth.AggregateDnResolver"> <constructor-arg index="0" ref="adDnResolvers" /> </bean> <bean id="adAggregateAuthHandler" class="org.ldaptive.auth.AggregateDnResolver$AuthenticationHandler" p:authenticationHandlers-ref="adAuthHandlers" /> <util:map id="adDnResolvers"> <entry key="directory_filter1" value-ref="adDnResolver1" /> <entry key="directory_filter2" value-ref="adDnResolver2" /> <entry key="directory_filter3" value-ref="adDnResolver3" /> <entry key="directory_filter4" value-ref="adDnResolver4" /> </util:map> <!-- Define four DN resolvers that bind search against the Directory --> <bean id="adDnResolver1" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN1}" p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter}" p:connectionFactory-ref="adBindSearchPooledConnectionFactory" /> <bean id="adDnResolver2" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN2}" p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter}" p:connectionFactory-ref="adBindSearchPooledConnectionFactory" /> <bean id="adDnResolver3" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN3}" p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter}" p:connectionFactory-ref="adBindSearchPooledConnectionFactory" /> <bean id="adDnResolver4" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN4}" p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter}" p:connectionFactory-ref="adBindSearchPooledConnectionFactory" /> <!-- Define Directory1 Search-pool --> <bean id="adBindSearchPooledConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory" p:connectionPool-ref="adBindSearchConnectionPool" /> <bean id="adBindSearchConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" parent="adConnectionPool" p:connectionFactory-ref="adBindSearchConnectionFactory" p:name="adSearch-pool" /> <bean id="adBindSearchConnectionFactory" class="org.ldaptive.DefaultConnectionFactory" p:connectionConfig-ref="adBindSearchConnectionConfig" /> <bean id="adBindSearchConnectionConfig" parent="adConnectionConfig" p:connectionInitializer-ref="adBindConnectionInitializer" /> <bean id="adBindConnectionInitializer" class="org.ldaptive.BindConnectionInitializer" p:bindDn="%{idp.authn.LDAP.bindDN}"> <property name="bindCredential"> <bean class="org.ldaptive.Credential"> <constructor-arg value="%{idp.authn.LDAP.bindDNCredential}" /> </bean> </property> </bean> <util:map id="adAuthHandlers"> <entry key="directory_filter1" value-ref="adAuthHandler1" /> <entry key="directory_filter2" value-ref="adAuthHandler1" /> <entry key="directory_filter3" value-ref="adAuthHandler1" /> <entry key="directory_filter4" value-ref="adAuthHandler1" /> </util:map> <!-- authentication handler for Directory DN resolver --> <bean id="adAuthHandler1" class="org.ldaptive.auth.PooledBindAuthenticationHandler" p:connectionFactory-ref="adBindPooledConnectionFactory" /> <bean id="adBindPooledConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory" p:connectionPool-ref="adBindConnectionPool" /> <bean id="adBindConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" parent="adConnectionPool" p:connectionFactory-ref="adBindConnectionFactory" p:name="adBind-pool" /> <bean id="adBindConnectionFactory" class="org.ldaptive.DefaultConnectionFactory" p:connectionConfig-ref="adBindConnectionConfig" /> <bean id="adBindConnectionConfig" parent="adConnectionConfig" />
Now we have to add the additional baseDN attributes that we defined and assign the other values in ldap.properties.
Info title Attribute Matching Note that we match on the attribute "mail" because of our structure, but I think most AD users would want to match on sAMAccountName if you want to match on just "username". If you'd rather match on "username@domain.com" then use userPrincipalName. Also we have idp.authn.LDAP.subtreeSearch set to false because we have OU's we dont want searched, but most people enable this.
Code Block language xml theme Confluence title ldap.properties collapse true # LDAP authentication configuration, see authn/ldap-authn-config.xml # Note, this doesn't apply to the use of JAAS ## Authenticator strategy, either anonSearchAuthenticator, bindSearchAuthenticator, directAuthenticator, adAuthenticator idp.authn.LDAP.authenticator = adAggregateAuthenticator ## Connection properties ## idp.authn.LDAP.ldapURL = ldaps://ad1.skool.edu:636 ldaps://ad2.skool.edu:636 idp.authn.LDAP.useStartTLS = false idp.authn.LDAP.useSSL = true idp.authn.LDAP.connectTimeout = 3000 ## SSL configuration, either jvmTrust, certificateTrust, or keyStoreTrust idp.authn.LDAP.sslConfig = certificateTrust ## If using certificateTrust above, set to the trusted certificate's path idp.authn.LDAP.trustCertificates = %{idp.home}/credentials/ldapserver.crt ## If using keyStoreTrust above, set to the truststore path idp.authn.LDAP.trustStore = %{idp.home}/credentials/ldapserver.truststore ## Return attributes during authentication ## NOTE: there is a separate property used for attribute resolution idp.authn.LDAP.returnAttributes = passwordExpirationTime,loginGraceRemaining ## DN resolution properties ## # Search DN resolution, used by anonSearchAuthenticator,bindSearchAuthenticator # for AD: CN=Users,DC=example,DC=org # idp.authn.LDAP.baseDN1 = OU=folder1,DC=skool,DC=edu idp.authn.LDAP.baseDN2 = OU=subfolder1,OU=folder2,DC=skool,DC=edu idp.authn.LDAP.baseDN3 = OU=folder3,DC=skool,DC=edu idp.authn.LDAP.baseDN4 = OU=folder4,DC=skool,DC=edu idp.authn.LDAP.subtreeSearch = false #idp.authn.LDAP.userFilter = (uid={user}) idp.authn.LDAP.userFilter = (mail={user}) # bind search configuration # for AD: idp.authn.LDAP.bindDN=adminuser@domain.com idp.authn.LDAP.bindDN = CN=ADadmin,ou=managers,dc=skool,dc=edu idp.authn.LDAP.bindDNCredential = secretsquirrel66!!! # Format DN resolution, used by directAuthenticator, adAuthenticator # for AD use idp.authn.LDAP.dnFormat=%s@domain.com idp.authn.LDAP.dnFormat = uid=%s,ou=people,dc=example,dc=org # LDAP attribute configuration, see attribute-resolver.xml # Note, this likely won't apply to the use of legacy V2 resolver configurations idp.attribute.resolver.LDAP.ldapURL = %{idp.authn.LDAP.ldapURL} idp.attribute.resolver.LDAP.baseDN1 = %{idp.authn.LDAP.baseDN1:undefined} idp.attribute.resolver.LDAP.baseDN2 = %{idp.authn.LDAP.baseDN2:undefined} idp.attribute.resolver.LDAP.baseDN3 = %{idp.authn.LDAP.baseDN3:undefined} idp.attribute.resolver.LDAP.baseDN4 = %{idp.authn.LDAP.baseDN4:undefined} idp.attribute.resolver.LDAP.bindDN = %{idp.authn.LDAP.bindDN:undefined} idp.attribute.resolver.LDAP.bindDNCredential = %{idp.authn.LDAP.bindDNCredential:undefined} idp.attribute.resolver.LDAP.useStartTLS = %{idp.authn.LDAP.useStartTLS:true} idp.attribute.resolver.LDAP.trustCertificates = %{idp.authn.LDAP.trustCertificates:undefined} #idp.attribute.resolver.LDAP.searchFilter = (uid=$resolutionContext.principal) idp.attribute.resolver.LDAP.searchFilter = (mail=$requestContext.principalName) idp.attribute.resolver.LDAP.returnAttributes = givenname,mail,samaccountname,sn
You should be able to login to test sites with your additional OU's now, but if your not seeing attributes released its because if you use the example attribute-resolver-ldap.xml for your attribute-resolver.xml file then there's only 1 DataConnector defined by default, and you have 4 (or more) DataConnectors. We'll replace the existing DataConnector with multiple DataConnectors in a failover
Code Block language xml title attribute-resolver.xml collapse true <resolver:DataConnector id="myLDAP" xsi:type="dc:LDAPDirectory" ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}" baseDN="%{idp.attribute.resolver.LDAP.baseDN1}" principal="%{idp.attribute.resolver.LDAP.bindDN}" principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}" noResultIsError="True" useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS::true}"> <resolver:FailoverDataConnector ref="myLDAP2" /> <dc:FilterTemplate> <![CDATA[ %{idp.attribute.resolver.LDAP.searchFilter} ]]> </dc:FilterTemplate> <dc:ReturnAttributes>%{idp.attribute.resolver.LDAP.returnAttributes}</dc:ReturnAttributes> <dc:LDAPProperty name="java.naming.referral" value="follow"/> <dc:StartTLSTrustCredential id="LDAPtoIdPCredential" xsi:type="sec:X509ResourceBacked"> <sec:Certificate>%{idp.attribute.resolver.LDAP.trustCertificates}</sec:Certificate> </dc:StartTLSTrustCredential> </resolver:DataConnector> <resolver:DataConnector id="myLDAP2" xsi:type="dc:LDAPDirectory" ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}" baseDN="%{idp.attribute.resolver.LDAP.baseDN2}" principal="%{idp.attribute.resolver.LDAP.bindDN}" principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}" noResultIsError="True" useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS::true}"> <resolver:FailoverDataConnector ref="myLDAP3" /> <dc:FilterTemplate> <![CDATA[ %{idp.attribute.resolver.LDAP.searchFilter} ]]> </dc:FilterTemplate> <dc:ReturnAttributes>%{idp.attribute.resolver.LDAP.returnAttributes}</dc:ReturnAttributes> <dc:LDAPProperty name="java.naming.referral" value="follow"/> <dc:StartTLSTrustCredential id="LDAPtoIdPCredential" xsi:type="sec:X509ResourceBacked"> <sec:Certificate>%{idp.attribute.resolver.LDAP.trustCertificates}</sec:Certificate> </dc:StartTLSTrustCredential> </resolver:DataConnector> <resolver:DataConnector id="myLDAP3" xsi:type="dc:LDAPDirectory" ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}" baseDN="%{idp.attribute.resolver.LDAP.baseDN3}" principal="%{idp.attribute.resolver.LDAP.bindDN}" principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}" noResultIsError="True" useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS::true}"> <resolver:FailoverDataConnector ref="myLDAP4" /> <dc:FilterTemplate> <![CDATA[ %{idp.attribute.resolver.LDAP.searchFilter} ]]> </dc:FilterTemplate> <dc:ReturnAttributes>%{idp.attribute.resolver.LDAP.returnAttributes}</dc:ReturnAttributes> <dc:LDAPProperty name="java.naming.referral" value="follow"/> <dc:StartTLSTrustCredential id="LDAPtoIdPCredential" xsi:type="sec:X509ResourceBacked"> <sec:Certificate>%{idp.attribute.resolver.LDAP.trustCertificates}</sec:Certificate> </dc:StartTLSTrustCredential> </resolver:DataConnector> <resolver:DataConnector id="myLDAP4" xsi:type="dc:LDAPDirectory" ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}" baseDN="%{idp.attribute.resolver.LDAP.baseDN4}" principal="%{idp.attribute.resolver.LDAP.bindDN}" principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}" noResultIsError="True" useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS::true}"> <dc:FilterTemplate> <![CDATA[ %{idp.attribute.resolver.LDAP.searchFilter} ]]> </dc:FilterTemplate> <dc:ReturnAttributes>%{idp.attribute.resolver.LDAP.returnAttributes}</dc:ReturnAttributes> <dc:LDAPProperty name="java.naming.referral" value="follow"/> <dc:StartTLSTrustCredential id="LDAPtoIdPCredential" xsi:type="sec:X509ResourceBacked"> <sec:Certificate>%{idp.attribute.resolver.LDAP.trustCertificates}</sec:Certificate> </dc:StartTLSTrustCredential> </resolver:DataConnector>
...