Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Current File(s): conf/authn/password-authn-config.xml, conf/ldap.properties, conf/authn/ldap-authn-config.xml (V4.0), conf/authn/authn.properties (V4.1+)
Format: Native Spring

...

Expand
titleV4.1+

Configuring LDAP as a back-end relies on beans internally that are configured using ldap.properties (defined separately from other properties because they are sometimes shared for LDAPConnector configuration). Older releases included an authn/ldap-authn-config.xml file; this remains supported but is no longer required or provided.

The properties in ldap.properties do most of the work out of the box. Adding additional beans may be needed in very advanced cases where a higher degree of control is required, and you are welcome to place them within authn/password-authn-config.xml.

The properties act as global defaults that can be overridden on specific instances of beans inheriting from shibboleth.LDAPValidator defined in authn/password-authn-config.xml in the shibboleth.authn.Password.Validators bean.

In the simple case of LDAP used alone:

Defining use of LDAP in password-authn-config.xml
Code Block
languagexml
<util:list id="shibboleth.authn.Password.Validators">
    <!-- Default bean uses the settings defined in ldap.properties -->
    <ref bean="shibboleth.LDAPValidator" />
</util:list>

If desired, itIt's possible to directly configure the various settings within the validator bean instead of or in addition to relying on the defaults. Typically this involves injecting a bean based on shibboleth.LDAPAuthenticationFactory into the validator bean’s authenticator property. This is a large factory class of type net.shibboleth.idp.authn.config.LDAPAuthenticationFactoryBean that includes most common LDAP settings.

As an example, you could chain together multiple LDAP servers (rather than hoping the client library will do it for you) like this:

Chaining LDAP validators
Code Block
languagexml
<!--
These use the settings defined in ldap-authn-config.xml and ldap.properties except the ones overridden here.
-->

<util:list id="shibboleth.authn.Password.Validators">
	<bean p:id="ldap1" parent="shibboleth.LDAPValidator">
    	<property name="authenticator">
			<bean parent="shibboleth.LDAPAuthenticationFactory" p:ldapUrl="ldaps://ldap1.example.org" />
		</property>
	</bean>
	<bean p:id="ldap2" parent="shibboleth.LDAPValidator">
    	<property name="authenticator">
			<bean parent="shibboleth.LDAPAuthenticationFactory" p:ldapUrl="ldaps://ldap2.example.org" />
		</property>
	</bean>
</util:list>

Note Regarding Upgrades

The ldap-authn-config.xml file from V3 has been removed, with the associated objects declared internally and using a large set of properties that will generally auto-configure the proper objects.

Note

Updating from the older ldap-authn-config.xml

While the older, longer file should work in most cases, it is a good idea to consider removing the file from the distribution.

Having said that, removing the file will break initially unless you also explicitly define the bean called shibboleth.authn.Password.Validators (shown above), which is present by default in password-authn-config.xml in new installs. For LDAP alone, the example above generally suffices.

One issue that does come up with the older file: the defaults around pooling validation in V3 were expressed numerically in seconds, and these numbers are interpreted in V4 as milliseconds. The proper syntax is really XML Duration syntax (PT5M == 5 minutes) and not numerically, but out of the box using the old file with V4 and not setting some of the pooling properties will result in dramatically frequent pool validation on the order of every half second. The logs will be very noisy so it's quite obvious.

To correct this, either remove the old file (and define the new bean noted above), or change the property defaults in the old file, or actually set the properties themselves rather than leaving them commented.

...

certificateTrust

Uses the idp.authn.LDAP.trustCertificates property to load a resource containing the trust anchors (such as a file of PEM-format certificates)

keyStoreTrust

Uses the idp.authn.LDAP.trustStore property to load a keystore containing the trust anchors

jvmTrust

Uses the default JVM trust anchors (the JVM-wide "cacerts" file)

disabled 4.3

Does not allow SSL or startTLS connections.

We have tentative plans to deprecate the “jvmTrust” option, which has already been removed from the attribute resolution side of the software, as it is bad practice and has been a source of serious security flaws.

...

Expand
titleAttribute Retrieval

LDAP attributes are returned as part of the authentication process and exposed in the LDAPResponseContext.

Property

Sample

Result

idp.authn.LDAP.returnAttributes

uid,

eduPersonAffiliation

Returns the uid and

eduPersonAffiliation attributes.

*

Returns all user attributes on the entry.

*,+

Returns all user and operational attributes on the entry.

1.1

No attribute returned. No search performed.

By default, attributes will be searched for using the same connection the user authenticated on. Therefore the user must have read on any attributes for those to be returned.

If you need access to attributes that user does not have read access to, then you must configure a connection pool that is authorized to read that data. The following configuration demonstrates how to add a new connection pool for that purpose.

Spring Configuration
Code Block
languagexml
<!-- Modify the authenticator to use the entry resolver -->
<bean name="anonSearchAuthenticator" class="org.ldaptive.auth.Authenticator" p:entryResolver-ref="bindSearchEntryResolver">
...
 
<!-- Add an entry resolver to read attributes -->
<bean id="bindSearchEntryResolver" class="org.ldaptive.auth.PooledSearchEntryResolver" p:connectionFactory-ref="entryResolverPooledConnectionFactory" />
<bean id="entryResolverPooledConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory" p:connectionPool-ref="entryResolverConnectionPool" />
<bean id="entryResolverConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool" p:connectionFactory-ref="entryResolverConnectionFactory" p:name="entry-resolver-pool" />
<bean id="entryResolverConnectionFactory" class="org.ldaptive.DefaultConnectionFactory" p:connectionConfig-ref="entryResolverConnectionConfig" />
<bean id="entryResolverConnectionConfig" parent="connectionConfig" p:connectionInitializer-ref="entryResolverConnectionInitializer" />
<bean id="entryResolverConnectionInitializer" class="org.ldaptive.BindConnectionInitializer" p:bindDn="%{idp.authn.LDAP.entryResolver.bindDN}">
    <property name="bindCredential">
        <bean class="org.ldaptive.Credential">
            <constructor-arg value="%{idp.authn.LDAP.entryResolver.bindDNCredential}" />
        </bean>
    </property>
</bean>

Add the idp.authn.LDAP.entryResolver.bindDN and idp.authn.LDAP.entryResolver.bindDNCredential properties to conf/ldap.properties and credentials/secrets.properties respectively. Then set idp.authn.LDAP.authenticator to anonSearchAuthenticator. to complete the configuration.

Note: if you're using the bindSearchAuthenticator and those credentials can be reused for entry resolution, then this configuration can be shortened by wiring the bindPooledConnectionFactory to the entry resolver.

Spring Configuration
Expand
titleDN Resolution

Single Directory with Multiple Branches

Extensible Matching

If your directory supports extensible matching, this is the easiest way to find users that are distributed over multiple branches.

Property

Value

Result

idp.authn.LDAP.userFilter

(&(|(ou:dn:=people)(ou:dn:=guests))(uid={user}))

Returns the entry that matches uid on either the people or guests branch.

idp.authn.LDAP.baseDN

dc=example,dc=org

The branch containing both ou=people and ou=guests.

idp.authn.LDAP.subtreeSearch

true

Enable subtree searching on the dc=example,dc=org branch.

Aggregate DN Resolver

This authenticator combines the results of multiple DN resolvers.

note

This example is likely not correct yet, needs expert review.

Code Block
languagexml
<!-- Connection Configuration -->
<bean nameid="aggregateAuthenticatorconnectionConfig" class="org.ldaptive.auth.AuthenticatorConnectionConfig">
     abstract="true" p:ldapUrl="%{idp.authn.LDAP.ldapURL}"
      p:useStartTLS="%{idp.authn.LDAP.useStartTLS:true}"
      p:useSSL="%{idp.authn.LDAP.useSSL:false}"
      p:connectTimeoutDuration="%{idp.authn.LDAP.connectTimeout:PT3S}"
      p:responseTimeoutDuration="%{idp.authn.LDAP.responseTimeout:PT3S}"
      p:sslConfig-ref="sslConfig" />

<alias name="%{idp.authn.LDAP.sslConfig:certificateTrust}" alias="sslConfig" />

<bean id="jvmTrust" class="org.ldaptive.ssl.SslConfig" />
<bean id="certificateTrust" class="org.ldaptive.ssl.SslConfig">
    <property name="credentialConfig">
        <bean parent="shibboleth.X509ResourceCredentialConfig" p:trustCertificates="%{idp.authn.LDAP.trustCertificates:undefined}" />
    </property>
</bean>
<bean id="keyStoreTrust" class="org.ldaptive.ssl.SslConfig">
    <property name="credentialConfig">
        <bean parent="shibboleth.KeystoreResourceCredentialConfig" p:truststore="%{idp.authn.LDAP.trustStore:undefined}" />
    </property>
</bean>

<!-- Pool Configuration -->
<bean id="connectionPool" class="org.ldaptive.pool.BlockingConnectionPool" abstract="true"
      p:blockWaitTimeDuration="%{idp.pool.LDAP.blockWaitTime:PT3S}"
      p:poolConfig-ref="poolConfig"
      p:pruneStrategy-ref="pruneStrategy"
      p:validator-ref="searchValidator"
      p:failFastInitialize="%{idp.pool.LDAP.failFastInitialize:false}" />
<bean id="poolConfig" 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:validatePeriodDuration="%{idp.pool.LDAP.validatePeriod:PT5M}" />
<bean id="pruneStrategy" class="org.ldaptive.pool.IdlePruneStrategy"
      p:prunePeriodDuration="%{idp.pool.LDAP.prunePeriod:PT5M}"
      p:idleTimeDuration="%{idp.pool.LDAP.idleTime:PT10M}" />
<bean id="searchValidator" class="org.ldaptive.pool.SearchValidator" />

<!-- Authentication handler -->
<bean id="authHandler" class="org.ldaptive.auth.PooledBindAuthenticationHandler" p:connectionFactory-ref="bindPooledConnectionFactory" />
<bean id="bindPooledConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory" p:connectionPool-ref="bindConnectionPool" />
<bean id="bindConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="bindConnectionFactory" p:name="bind-pool" />
<bean id="bindConnectionFactory" class="org.ldaptive.DefaultConnectionFactory" p:connectionConfig-ref="bindConnectionConfig" />
<bean id="bindConnectionConfig" parent="connectionConfig" />

<!-- Anonymous Search Configuration -->
<bean name="anonSearchAuthenticator" class="org.ldaptive.auth.Authenticator" p:resolveEntryOnFailure="%{idp.authn.LDAP.resolveEntryOnFailure:false}">
    <constructor-arg index="0" ref="anonSearchDnResolver" />
    <constructor-arg index="1" ref="authHandler" />
</bean>
<bean id="anonSearchDnResolver" class="net.shibboleth.idp.authn.PooledTemplateSearchDnResolver"
      p:baseDn="#{'%{idp.authn.LDAP.baseDN:undefined}'.trim()}"
      p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}"
      p:connectionFactory-ref="anonSearchPooledConnectionFactory" >
    <constructor-arg index="0" ref="shibboleth.VelocityEngine" />
    <constructor-arg index="1" value="#{'%{idp.authn.LDAP.userFilter:undefined}'.trim()}" />
</bean>
<bean id="anonSearchPooledConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory"
      p:connectionPool-ref="anonSearchConnectionPool" />
<bean id="anonSearchConnectionPool" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="anonSearchConnectionFactory" p:name="search-pool" />
<bean id="anonSearchConnectionFactory" class="org.ldaptive.DefaultConnectionFactory" p:connectionConfig-ref="anonSearchConnectionConfig" />
<bean id="anonSearchConnectionConfig" parent="connectionConfig" />

<bean name="aggregateAuthenticator" class="org.ldaptive.auth.Authenticator">
    <constructor-arg index="0" ref="aggregateDnResolver" />
    <constructor-arg index="1" ref="aggregateAuthHandler" />
</bean>
<bean id="aggregateDnResolver" class="org.ldaptive.auth.AggregateDnResolver">
    <constructor-arg index="0" ref="dnResolvers" />
</bean>
<bean id="aggregateAuthHandler" class="org.ldaptive.auth.AggregateDnResolver$AuthenticationHandler" p:authenticationHandlers-ref="authHandlers" />
<util:map id="dnResolvers">
    <entry key="filter1" value-ref="dnResolver1" />
    <entry key="filter2" value-ref="dnResolver2" />
</util:map>

<!-- Define two DN resolvers that use anonymous search against the same directory -->
<bean id="dnResolver1" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN1}"
      p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter1}"
      p:connectionFactory-ref="anonSearchPooledConnectionFactory" />
<bean id="dnResolver2" class="org.ldaptive.auth.PooledSearchDnResolver" p:baseDn="%{idp.authn.LDAP.baseDN2}"
      p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}" p:userFilter="%{idp.authn.LDAP.userFilter2}"
      p:connectionFactory-ref="anonSearchPooledConnectionFactory" />
<!-- Use the same authentication handler for both DN resolvers -->
<util:map id="authHandlers">
    <entry key="filter1" value-ref="authHandler" />
    <entry key="filter2" value-ref="authHandler" />
</util:map>

Add the idp.authn.LDAP.baseDN[12] and idp.authn.LDAP.userFilter[12] properties to conf/ldap.properties. The key values used in dnResolvers and authHandlers can be anything, but must tie a single DN resolver to a single auth handler. By default an error will occur if more than (1) DN is resolved.

In addition to these beans, the aggregateAuthenticator bean (in the example above) must be injected into the LDAP credential validator in password-authn-config.xml:

Code Block
<util:list id="shibboleth.authn.Password.Validators">
    <!-- Default bean uses the settings defined in ldap.properties -->
    <bean parent="shibboleth.LDAPValidator" p:authenticator-ref="aggregateAuthenticator" />
</util:list>

Multiple Directories

Aggregate DN Resolver

This authenticator combines the results of multiple DN resolvers that connect to multiple directories.

Spring Configuration
Code Block
languagexml
<bean name="aggregateAuthenticator" class="org.ldaptive.auth.Authenticator">
    <constructor-arg index="0" ref="aggregateDnResolver" />
    <constructor-arg index="1" ref="aggregateAuthHandler" />
</bean>
<bean id="aggregateDnResolver" class="org.ldaptive.auth.AggregateDnResolver">
    <constructor-arg index="0" ref="dnResolvers" />
</bean>
<bean id="aggregateAuthHandler" class="org.ldaptive.auth.AggregateDnResolver$AuthenticationHandler" p:authenticationHandlers-ref="authHandlers" />
<util:map id="dnResolvers">
    <entry key="directory1" value-ref="dnResolver1" />
    <entry key="directory2" value-ref="dnResolver2" />
</util:map>
<util:map id="authHandlers">
    <entry key="directory1" value-ref="authHandler1" />
    <entry key="directory2" value-ref="authHandler2" />
</util:map>
<!-- define DN resolvers and authentication handlers for each directory... -->

Complete example with DN resolvers and authentication handlers for bindSearch

Code Block
languagexml
<!-- Properties:
- idp.authn.LDAP.authenticator = aggregateAuthenticator
- idp.authn.LDAP.ldapURL1
- idp.authn.LDAP.ldapURL2
- idp.authn.LDAP.baseDN1
- idp.authn.LDAP.baseDN2
- idp.authn.LDAP.userFilter1
- idp.authn.LDAP.userFilter2
- idp.authn.LDAP.bindDN1
- idp.authn.LDAP.bindDN2
- idp.authn.LDAP.bindDNCredential1
- idp.authn.LDAP.bindDNCredential2
-->
<bean id="aggregateAuthenticator" class="org.ldaptive.auth.Authenticator"
      c:resolver-ref="aggregateDnResolver"
      c:handler-ref="aggregateAuthHandler" />

<!-- Aggregate DN resolution -->
<bean id="aggregateDnResolver" class="org.ldaptive.auth.AggregateDnResolver"
      c:resolvers-ref="dnResolvers"
      p:allowMultipleDns="true" />
<util:map id="dnResolvers">
    <entry key="directory1" value-ref="bindSearchDnResolver1" />
    <entry key="directory2" value-ref="bindSearchDnResolver2" />
</util:map>

<!-- DN resolver 1 -->
<bean id="bindSearchDnResolver1" class="org.ldaptive.auth.PooledSearchDnResolver"
      p:baseDn="#{'%{idp.authn.LDAP.baseDN1:undefined}'.trim()}"
      p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}"
      p:userFilter="#{'%{idp.authn.LDAP.userFilter1:undefined}'.trim()}"
      p:connectionFactory-ref="bindSearchPooledConnectionFactory" />
<bean id="bindSearchPooledConnectionFactory1" class="org.ldaptive.pool.PooledConnectionFactory"
      p:connectionPool-ref="bindSearchConnectionPool1" />
<bean id="bindSearchConnectionPool1" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="bindSearchConnectionFactory1"
      p:name="search-pool1" />
<bean id="bindSearchConnectionFactory1" class="org.ldaptive.DefaultConnectionFactory"
      p:connectionConfig-ref="bindSearchConnectionConfig1" />
<bean id="bindSearchConnectionConfig1" parent="connectionConfig"
      p:connectionInitializer-ref="bindConnectionInitializer1"
      p:ldapUrl="%{idp.authn.LDAP.ldapURL1}" />
<bean id="bindConnectionInitializer1" class="org.ldaptive.BindConnectionInitializer"
      p:bindDn="#{'%{idp.authn.LDAP.bindDN1:undefined}'.trim()}">
    <property name="bindCredential">
    <bean class="org.ldaptive.Credential" c:password="%{idp.authn.LDAP.bindDNCredential1:undefined}" />
    </property>
</bean>
<!-- DN resolver 2 -->
<bean id="bindSearchDnResolver2" class="org.ldaptive.auth.PooledSearchDnResolver"
      p:baseDn="#{'%{idp.authn.LDAP.baseDN2:undefined}'.trim()}"
      p:subtreeSearch="%{idp.authn.LDAP.subtreeSearch:false}"
      p:userFilter="#{'%{idp.authn.LDAP.userFilter2:undefined}'.trim()}"
      p:connectionFactory-ref="bindSearchPooledConnectionFactory" />
<bean id="bindSearchPooledConnectionFactory2" class="org.ldaptive.pool.PooledConnectionFactory"
      p:connectionPool-ref="bindSearchConnectionPool2" />
<bean id="bindSearchConnectionPool2" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="bindSearchConnectionFactory2"
      p:name="search-pool2" />
<bean id="bindSearchConnectionFactory2" class="org.ldaptive.DefaultConnectionFactory"
      p:connectionConfig-ref="bindSearchConnectionConfig2" />
<bean id="bindSearchConnectionConfig2" parent="connectionConfig"
      p:connectionInitializer-ref="bindConnectionInitializer2"
      p:ldapUrl="%{idp.authn.LDAP.ldapURL2}" />
<bean id="bindConnectionInitializer2" class="org.ldaptive.BindConnectionInitializer"
      p:bindDn="#{'%{idp.authn.LDAP.bindDN2:undefined}'.trim()}">
    <property name="bindCredential">
        <bean class="org.ldaptive.Credential" c:password="%{idp.authn.LDAP.bindDNCredential2:undefined}" />
    </property>
</bean>

<!-- Aggregate authentication -->
<bean id="aggregateAuthHandler" class="org.ldaptive.auth.AggregateDnResolver$AuthenticationHandler"
      p:authenticationHandlers-ref="authHandlers" />
<util:map id="authHandlers">
    <entry key="directory1" value-ref="authHandler1" />
    <entry key="directory2" value-ref="authHandler2" />
</util:map>

<!-- Authentication handler 1 -->
<bean id="authHandler1" class="org.ldaptive.auth.PooledBindAuthenticationHandler"
      p:connectionFactory-ref="bindPooledConnectionFactory1" />
<bean id="bindPooledConnectionFactory1" class="org.ldaptive.pool.PooledConnectionFactory"
      p:connectionPool-ref="bindConnectionPool1" />
<bean id="bindConnectionPool1" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="bindConnectionFactory1"
      p:name="bind-pool1" />
<bean id="bindConnectionFactory1" class="org.ldaptive.DefaultConnectionFactory"
      p:connectionConfig-ref="bindConnectionConfig1" />
<bean id="bindConnectionConfig1" parent="connectionConfig"
      p:ldapUrl="%{idp.authn.LDAP.ldapURL1}" />
<!-- Authentication handler 2 -->
<bean id="authHandler2" class="org.ldaptive.auth.PooledBindAuthenticationHandler"
      p:connectionFactory-ref="bindPooledConnectionFactory2" />
<bean id="bindPooledConnectionFactory2" class="org.ldaptive.pool.PooledConnectionFactory"
      p:connectionPool-ref="bindConnectionPool2" />
<bean id="bindConnectionPool2" class="org.ldaptive.pool.BlockingConnectionPool" parent="connectionPool"
      p:connectionFactory-ref="bindConnectionFactory2"
      p:name="bind-pool2" />
<bean id="bindConnectionFactory2" class="org.ldaptive.DefaultConnectionFactory"
      p:connectionConfig-ref="bindConnectionConfig2" />
<bean id="bindConnectionConfig2" parent="connectionConfig"
      p:ldapUrl="%{idp.authn.LDAP.ldapURL2}" />

DN resolvers are invoked asynchronously, so all resolvers will be used for each authentication request.

...