Skip to content

LDAP Authentication

mbarto edited this page Jan 28, 2015 · 8 revisions

Configuring LDAP Authentication

General configuration

Authenticating using an LDAP repository requires:

  • adding a specific authentication-provider in the authentication-manager section of the geostore-spring-security file:
<security:authentication-manager>
    <security:authentication-provider ref='geostoreLdapProvider' />
</security:authentication-manager>
  • properly configure the provider (with appropriate Authenticator and AuthoritiesPopulator):
<bean id="geostoreLdapProvider"
    class="it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider">
    <constructor-arg>
        <bean
            class="org.springframework.security.ldap.authentication.BindAuthenticator">
            ...
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean
            class="it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator">
            ...
        </bean>
    </constructor-arg>
    ...
</bean>

UserLdapAuthenticationProvider is an extension of the standard LdapAuthenticationProvider, implementing automatic synch of users on the Geostore internal database.

  • properly configure the contextSource:
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://localhost:389/dc=test,dc=local" />
</bean>

The contextSource specifies the connection settings to the LDAP repository (host, port, base root).

Configuring the Authenticator

The Authenticator is responsible for credentials checking.

For LDAP the BindAuthenticator is the most common choice and checks credentials doing a bind action on the LDAP repository with the given username and password.

You need to configure a contextSource (LDAP repository connection settings) and a filter for user searching.

The filter needs:

  • a base folder for searching users (e.g. ou=people)
  • a filter using the given username ({0}) to match a repository object (e.g. uid={0})
<bean id="geostoreLdapProvider"
    class="it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider">
    <constructor-arg>
        <bean
            class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <constructor-arg ref="contextSource" />
            <property name="userSearch">
                <bean id="userSearch"
                    class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
                    <constructor-arg index="0" value="ou=people" />
                    <constructor-arg index="1" value="(uid={0})" />
                    <constructor-arg index="2" ref="contextSource" />
                </bean>
            </property>
        </bean>
    </constructor-arg>
    ...
</bean>

Configuring the AuthoritiesPopulator

The AuthoritiesPopulator is responsible for adding authorities (groups and roles) to the authenticated user, extracting data from the LDAP repository.

<bean id="geostoreLdapProvider"
		class="it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider">
		...
    <constructor-arg>
        <bean
            class="it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator">
            <constructor-arg ref="contextSource" />
            <!-- groupSearchBase -->
            <constructor-arg value="ou=groups" />
            <!-- roleSearchBase -->
            <constructor-arg value="ou=roles" />
            <property name="groupSearchFilter" value="(member={0})" />
            <property name="roleSearchFilter" value="(member={0})" />
            
            <property name="rolePrefix" value="ROLE_" />
            <property name="searchSubtree" value="true" />
            <property name="convertToUpperCase" value="true" />
        </bean>
    </constructor-arg>
</bean>

GeoStoreLdapAuthoritiesPopulator extends the standard LdapAuthoritiesPopulator, implementing automatic synch of group on the Geostore internal database.

GeoStoreLdapAuthoritiesPopulator can be configured to extract groups from a specific root folder (e.g. ou=groups), using a specific filter (groupSearchFilter) to look for user membership (e.g. member={0}, where {0} is the user dn).

The same can be done for roles, using a different root folder (e.g. ou=roles) and filter (roleSearchFilter). Roles can have a fixed prefix applied to them (e.g. ROLE_).

Note that roles have to match one of the 3 standard GeoStore roles to be accepted (ROLE_ADMIN, ROLE_USER or ROLE_GUEST).

To configure matching at a finer level an authoritiesMapper can be used.

Attributes mapping

Optionally, LDAP user attributes can be extracted to fill GeoStore user attributes.

This can be done using:

  • a userDetailsContextMapper to extract specified attributes from the LDAP user object (the source)
  • a userMapper to fill specific attributes getting values from the source
<bean id="geostoreLdapProvider"
    class="it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider">
    ...
    <property name="userDetailsContextMapper">
        <bean class="it.geosolutions.geostore.core.security.ldap.CustomAttributesLdapUserDetailsMapper">
            <constructor-arg>
                <map>
                    <entry key="FullName" value="cn"/>
                </map>
            </constructor-arg>
        </bean>
    </property>
    <property name="userMapper">
        <bean class="it.geosolutions.geostore.core.security.ExpressionUserMapper">
            <constructor-arg>
                <map>
                    <entry key="FullName" value="FullName"/>
                    <entry key="UUID" value="T(java.util.UUID).randomUUID().toString()"/>
                </map>
            </constructor-arg>
        </bean>
    </property>
</bean>

In this example the cn LDAP attribute is mapped to FullName and then used as a GeoStore user attribute. Moreover a static attribute (UUID) is created with a random value (usable to create an authkey for GeoServer authentication).

The mapping is done using an ExpressionUserMapper, that can leverage SpEL expression.

Multiple groups / roles search folders

More than one base folder can be specified for groups / roles search. To do that, just list them separated by commas.

<bean id="geostoreLdapProvider"
		class="it.geosolutions.geostore.services.rest.security.UserLdapAuthenticationProvider">
		...
    <constructor-arg>
        <bean
            class="it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator">
            <constructor-arg ref="contextSource" />
            <!-- groupSearchBase -->
            <constructor-arg value="ou=groups,ou=othergroups" />
            <!-- roleSearchBase -->
            <constructor-arg value="ou=roles,out=otherroles" />
        </bean>
    </constructor-arg>
</bean>

LDAP as the main authenticator with GeoStore db as a fallback

Spring Security allows more than one Authentication Provider to be configured in sequence, so for example LDAP can be configured together with the GeoStore internal db authentication provider as a fallback.

<security:authentication-manager>
    <security:authentication-provider ref='geostoreLdapProvider' />
    <security:authentication-provider ref='geoStoreUserServiceAuthenticationProvider' />
</security:authentication-manager>

Beware that if a username exists on both database (and it will happen for sure, since LDAP users are automatically synched on the GeoStore database, login is tried both directly on LDAP and on the db).

ActiveDirectory support

To support Microsoft ActiveDirectory some configuration hints are needed:

  • the contextSource needs to specify a username / password combination for a user with browsing permissions on the ActiveDirectory tree (this is because AD doesn't allow anonymous browsing, but browsing is needed for full user bind authentication and authorities population)
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://localhost:389/dc=domain,dc=company,dc=local" />
    <property name="userDn" value="Administrator@domain.company.local"/>
    <property name="password" value="password"/>
</bean>
  • specific values are needed for BindAuthenticator base folder (cn=Users) and user search filter (sAMAccountName={0}):
<bean
    class="org.springframework.security.ldap.authentication.BindAuthenticator">
    ...
    <property name="userSearch">
        <bean id="userSearch"
            class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
            <constructor-arg index="0" value="cn=Users" />
            <constructor-arg index="1" value="(sAMAccountName={0})" />
            <constructor-arg index="2" ref="contextSource" />
        </bean>
    </property>
</bean>
  • Administrative groups (also more than one) can be mapped to GeoStore ADMIN role:
<bean
    class="it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator">
    ...
    <property name="authoritiesMapper">
        <bean
            class="it.geosolutions.geostore.core.security.SimpleGrantedAuthoritiesMapper">
            <constructor-arg>
                <map>
                    <entry key="ROLE_ADMINISTRATORS" value="ROLE_ADMIN"/>
                    <entry key="ROLE_DOMAIN ADMINS" value="ROLE_ADMIN"/>
                </map>
            </constructor-arg>
        </bean>
    </property>
</bean>
  • more than one LDAP base folder (usually at least CN=Builtin, but more can be added) could need to be configured, using the comma delimited list syntax:
<bean
    class="it.geosolutions.geostore.services.rest.security.GeoStoreLdapAuthoritiesPopulator">
    ...
    <!-- groupSearchBase -->
    <constructor-arg value="CN=Builtin,OU=Groups" />
    <!-- roleSearchBase -->
    <constructor-arg value="CN=Builtin" />
    ...
</bean>