How to configure usermanagement to communicate with an LDAP directory server?

Tested setup

This configuration worked for:

  • Archiva 1.2 standalone
  • Windows XP
  • Active Directory as LDAP directory server

How to configure

Creating local admin

  • It is very important to have a local admin. You can create this user while starting up the application for the first time. Open a command prompt and change the current directory to ARCHIVA_HOME:
$ cd %ARCHIVA_HOME%
$ %ARCHIVA_HOME%/bin/archiva start
Username          :  admin
Full Name         :  ##SOME NAME##
Email Address     :  ##SOME E-MAIL ADDRESS##
Password          :  ##SOME PASSWORD##
Confirm Password  :  ##SOME PASSWORD##
  • Log in with the admin account you've just created
  • Now stop your application by pushing "control + c" at the command line

Changing the configuration files

apache-archiva-x.y\apps\archiva\WEB-INF\classes\META-INF\plexus\application.xml

  • All components are already present in the XML file. You just need to uncomment and personalize them
  • Search the file for:
<!-- START SNIPPET: ldap -->
Component managing the connection to the ldap server
<component>
    <role>org.codehaus.plexus.redback.common.ldap.connection.LdapConnectionFactory</role>
    <role-hint>configurable</role-hint>
    <implementation>org.codehaus.plexus.redback.common.ldap.connection.ConfigurableLdapConnectionFactory</implementation>
    <configuration>
        <hostname>##HOSTNAME##</hostname>
        <port>##PORT##</port>
        <ssl>##SSL##</ssl>
        <baseDn>##BASEDN##</baseDn>
        <contextFactory>com.sun.jndi.ldap.LdapCtxFactory</contextFactory>
        <bindDn>##BINDDN##</bindDn>
        <password>##PASSWORD##</password>
    </configuration>
    <requirements>
        <requirement>
            <role>org.codehaus.plexus.redback.configuration.UserConfiguration</role>
        </requirement>
   </requirements>
</component>

name

description

example

HOSTNAME

The hostname of the ldap server

ldapserver.mycompany.be

PORT

The port of the ldap server

389

SSL

If you want to use SSL for connection to LDAP server

true

BASEDN

The baseDn of the ldap system

DC=mycompany,DC=be

BINDN

the core user used for authentication the ldap server, must be able to perform the necessary searches, etc.

CN=archiva,OU=User Accounts,DC=mycompany,DC=be

PASSWORD

password for the bindDn for the root ldap connection

xxxxx

Component managing the mapping of attributes in ldap to user information in redback
<component>
    <role>org.codehaus.plexus.redback.common.ldap.UserMapper</role>
    <role-hint>ldap</role-hint>
    <implementation>org.codehaus.plexus.redback.common.ldap.LdapUserMapper</implementation>
    <configuration>
        <email-attribute>##EMAIL-ATTRIBUTE##</email-attribute>
        <full-name-attribute>##FULL-NAME-ATTRIBUTE##</full-name-attribute>
        <password-attribute>##PASSWORD-ATTRIBUTE##</password-attribute>
        <user-id-attribute>##USER-ID-ATTRIBUTE##</user-id-attribute>
        <user-base-dn>##USER-BASE-DN##</user-base-dn>
        <user-object-class>inetOrgPerson</user-object-class>
    </configuration>
    <requirements>
        <requirement>
            <role>org.codehaus.plexus.redback.configuration.UserConfiguration</role>
        </requirement>
    </requirements>
</component>

name

description

example

email-attribute

The name of the attribute on a user that contains the email address

email

full-name-attribute

The name of the attribute on a user that contains the users fullName

givenName

password-attribute

The name of the attribute containing the users password, used for the authentiction using the user manager and not the ldap bind authenticator

userPassword

user-id-attribute

The name of the attribute containing the users userId, most commonly cn or sn

  • sAMAccountName 
  • cn
  • sn

user-base-dn

The base dn that will be subtree searched for users

DC=mycompany,DC=be

user-object-class

the objectClass used in the ldap server for indentifying users, most commonly inetOrgPerson

  • organizationalPerson
  • inetOrgPerson

apache-archiva-x.y\apps\archiva\WEB-INF\classes\org\apache\maven\archiva\security.properties

  • Add the following lines in this file (don't forget to change redback.default.admin)
user.manager.impl=ldap
ldap.bind.authenticator.enabled=true
redback.default.admin=##ADMIN_ACCOUNT_FOR_ARCHIVA##
redback.default.guest=guest
security.policy.password.expiration.enabled=false

The user.manager.impl is the role hint that is used to determine which user manaher to use while running. The default is 'cached' and if this is desired to be used with ldap then you must include the component declartion below in the caching section for the cached UserManager that sets the underlying userImpl to ldap.

The ldap.bind.authenitcator.enabled boolean value will toggle the use of authenticator that will authenticate using the bind operation. There are two different mechanisms used to authenticate with ldap, either the bind authenticator which is a standard way to authentication, and then the user manager password validation approach. If this is desired then you must ensure that the security policy is configured to use the correct password encoding. Normally the bind authenticator is simply enabled since this bypasses concerns of password encoding.

It is also now possible to redefine the basic admin user and guest user names. Since its unlikely that ldap oriented authentication systems will have a specific admin or guest user these can be redefined simply in the security.properties. Care must be taken that they exist in the ldap system since they are looked up. Guest users can be simple utilitie or application users.

The final setting of security.policy.password.expiration.enabled is a boolean that should be set to false for ldap based authentication. This is because redback will want to attempt to manage and enforce password expiration and that is no longer under the direction of redback but is
an artifact of the ldap system in place. Setting this to false prevents issues from cropping up related to redback trying to obtain this type of information.

How to use

  • Startup your Archiva application:
$ cd %ARCHIVA_HOME%
$ %ARCHIVA_HOME%/bin/archiva start

Defining groups

It's not possible to create groups in your LDAP Directory Server and then map them to the application roles. You will have a list of all the available users in the baseDn, and you will have to select a user and provide him the appropriate roles

  • In the left menu, click: User management
  • You will see a list of users
  • Click on a user
  • Define roles for that user

FAQ

What if a user gets deleted in LDAP, and the user has roles in Archiva?

Answer:
I'm not sure how the Redback LDAP application works (you can check the source code), but we tried it. The application keeps running after deleting the user.


I'm getting the following stacktrace:

org.codehaus.plexus.redback.common.ldap.connection.LdapException:
Could not connect to the server. [Root exception is
javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308:
LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data
525, vece]]
            at org.codehaus.plexus.redback.common.ldap.connection.LdapConnection.<init>(LdapConnection.java:81)
            at org.codehaus.plexus.redback.common.ldap.connection.ConfigurableLdapConnectionFactory.getConnection(ConfigurableLdapConnectionFactory.java:130)
            at org.codehaus.plexus.redback.users.ldap.LdapUserManager.newDirContext(LdapUserManager.java:338)
            at org.codehaus.plexus.redback.users.ldap.LdapUserManager.findUser(LdapUserManager.java:214)
            at org.codehaus.plexus.redback.users.configurable.ConfigurableUserManager.findUser(ConfigurableUserManager.java:111)
            at org.codehaus.plexus.redback.xwork.checks.security.GuestUserEnvironmentCheck.validateEnvironment(GuestUserEnvironmentCheck.java:82)
            at org.apache.maven.archiva.web.startup.SecuritySynchronization.executeEnvironmentChecks(SecuritySynchronization.java:151)
            at org.apache.maven.archiva.web.startup.SecuritySynchronization.startup(SecuritySynchronization.java:125)
            at org.apache.maven.archiva.web.startup.ArchivaStartup.contextInitialized(ArchivaStartup.java:56)
            at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:539)
            at org.mortbay.jetty.servlet.Context.startContext(Context.java:135)
            at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1216)
            at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:509)
            at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:447)
            at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:40
            at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:147)
            at org.mortbay.jetty.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:156)
            at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:40)
            at org.mortbay.jetty.handler.HandlerCollection.doStart(HandlerCollection.java:147)
            at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:40)
            at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:117)
            at org.mortbay.jetty.Server.doStart(Server.java:222)
            at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:40)
            at org.mortbay.xml.XmlConfiguration.main(XmlConfiguration.java:977)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at org.mortbay.start.Main.invokeMain(Main.java:194)
            at org.mortbay.start.Main.start(Main.java:509)
            at org.mortbay.start.Main.main(Main.java:119)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
            at java.lang.Thread.run(Unknown Source)

Caused by: javax.naming.AuthenticationException: [LDAP: error code 49
- 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext
error, data 525, vece

Answer:
We had this problem with Archiva 1.1.2. That version uses Redback LDAP 1.0.3. Apparently, there seems to be a bug in that version. Opening the baseDn, adds another "," at the end of the name. You can find more here.
You can upgrade Redback LDAP to 1.1, or use Archiva 1.2


I am sure that user credentials and server address + port are correct, why connection is not working?
Here is my exception from archiva.log:

2009-07-14 18:39:11,562 [btpool0-3] WARN  org.codehaus.plexus.redback.users.ldap.LdapUserManager  - failed to get a ldap connection Could not connect to the server.
org.codehaus.plexus.redback.common.ldap.connection.LdapException: Could not connect to the server. [Root exception is javax.naming.ServiceUnavailableException: ldapdc:636; socket closed]
        at org.codehaus.plexus.redback.common.ldap.connection.LdapConnection.<init>(LdapConnection.java:85)
        at org.codehaus.plexus.redback.common.ldap.connection.ConfigurableLdapConnectionFactory.getConnection(ConfigurableLdapConnectionFactory.java:133)
        at org.codehaus.plexus.redback.users.ldap.LdapUserManager.getLdapConnection(LdapUserManager.java:398)
        at org.codehaus.plexus.redback.users.ldap.LdapUserManager.findUser(LdapUserManager.java:173)
        at org.codehaus.plexus.redback.users.configurable.ConfigurableUserManager.findUser(ConfigurableUserManager.java:115)
        at org.codehaus.plexus.redback.struts2.interceptor.ForceAdminUserInterceptor.intercept(ForceAdminUserInterceptor.java:82)
        at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:224)
        at com.opensymphony.xwork2.DefaultActionInvocation$2.doProfiling(DefaultActionInvocation.java:223)
        at com.opensymphony.xwork2.util.profiling.UtilTimerStack.profile(UtilTimerStack.java:455)
        at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:221)
        at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:50)
        at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:504)
        at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:419)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
        at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:118)
        at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:52)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
        at org.apache.struts2.dispatcher.ActionContextCleanUp.doFilter(ActionContextCleanUp.java:99)
        at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
        at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
        at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
        at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
        at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:722)
        at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:404)
        at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
        at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
        at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
        at org.mortbay.jetty.Server.handle(Server.java:324)
        at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
        at org.mortbay.jetty.ajp.Ajp13Connection.access$2700(Ajp13Connection.java:42)
        at org.mortbay.jetty.ajp.Ajp13Connection$RequestHandler.headerComplete(Ajp13Connection.java:221)
        at org.mortbay.jetty.ajp.Ajp13Parser.parseNext(Ajp13Parser.java:474)
        at org.mortbay.jetty.ajp.Ajp13Parser.parseAvailable(Ajp13Parser.java:142)
        at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
        at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
        at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:450)
Caused by: javax.naming.ServiceUnavailableException: ldapdc:636; socket closed
        at com.sun.jndi.ldap.Connection.readReply(Connection.java:410)
        at com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:340)
        at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:192)
        at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2637)
        at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:283)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193)
        at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136)
        at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66)
        at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
        at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:247)
        at javax.naming.InitialContext.init(InitialContext.java:223)
        at javax.naming.InitialContext.<init>(InitialContext.java:197)
        at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:82)
        at org.codehaus.plexus.redback.common.ldap.connection.LdapConnection.<init>(LdapConnection.java:81)
        ... 35 more

Answer:
It is likely that your server uses SSL, and you forgot to specify it. Check Component managing the connection to the ldap server paragraph.

External information

This document has been created with the use of following links:

Comments;

Comment 1

I finally got this successfully setup.

  • Archiva 1.2.2 standalone
  • CentOs 4.x
  • Windows Active Directory via LDAP
  • apache-archiva-1.2.2/apps/archiva/WEB-INF/classes/META-INF/plexus/application.xml
    <component>
        <role>org.codehaus.plexus.redback.common.ldap.connection.LdapConnectionFactory</role>
        <role-hint>configurable</role-hint>
        <implementation>org.codehaus.plexus.redback.common.ldap.connection.ConfigurableLdapConnectionFactory</implementation>
        <configuration>
            <hostname>XXXX</hostname>
            <port>XXXX</port>
            <baseDn>XXXX</baseDn>
            <contextFactory>com.sun.jndi.ldap.LdapCtxFactory</contextFactory>
            <bindDn>XXXX</bindDn>
            <password><![CDATA[XXXX]]></password>
        </configuration>
         <requirements>
          <requirement>
           <role>org.codehaus.plexus.redback.configuration.UserConfiguration</role>
          </requirement>
         </requirements>
    </component>
    
    
    <component>
         <role>org.codehaus.plexus.redback.common.ldap.UserMapper</role>
         <role-hint>ldap</role-hint>
         <implementation>org.codehaus.plexus.redback.common.ldap.LdapUserMapper</implementation>
         <configuration>
          <email-attribute>mail</email-attribute>
          <full-name-attribute>displayName</full-name-attribute>
          <password-attribute>unicodePwd</password-attribute>
          <user-id-attribute>sAMAccountName</user-id-attribute>
          <user-base-dn>XXXX</user-base-dn>
          <user-object-class>user</user-object-class>
         </configuration>
         <requirements>
          <requirement>
           <role>org.codehaus.plexus.redback.configuration.UserConfiguration</role>
          </requirement>
         </requirements>
    </component>
    
    
  • apache-archiva-1.2.2/apps/archiva/WEB-INF/classes/org/apache/maven/archiva/security.properties
    user.manager.impl=ldap
    ldap.bind.authenticator.enabled=true
    redback.default.admin=XXXX
    #redback.default.guest=
    security.policy.password.expiration.enabled=false
    
  • No labels