Skip to content

Using Hudson Teams, Part 2 – Mapping Roles to Teams

September 4, 2013

In the last article in this series I looked at the basics of setting up and using the new Hudson 3.1.0 Team Concept feature (Using Hudson Teams, Part 1 – The Basics). In that article I dealt with the simple case of mapping users into teams, based around Hudson’s own internal user store. Now that approach is all very well with a small team to deal with, but once you move up to mid-sized and enterprise organizations it stops being very usable. The problem is, of course, one of the scale of user management; you simply don’t want to have to deal with the mechanics of  dealing with hundreds of individual users through Hudson’s web user interface.

The other thing to consider, is that within a single company or organization you may conceptually already have organization units or roles already defined within your global identity store. These existing roles may already contain the mapping of users that you need, and you have no desire to duplicate all of that work within Hudson.

The good news is, that the Team feature in Hudson, not only allows you to map users into teams but also Roles or Groups from any of the supported authentication providers as well.

To illustrate this I’ll take you through the basics of setting up an LDAP provider with some roles and then mapping those into the same teams I set up in Part 1.

Configuring LDAP

If you already have a suitable organization LDAP repository then you can go ahead and us that, however, for the sake of example here  I’m setting a new one up.  In this case I’ve used OpenLDAP from having installed this on my machine (OS/X in this case) it needs a little configuration to set it up to illustrate the feature.

For the purpose of this example I’ll be defining an LDAP repository with a root Distinguished Name (DN) of org.hudson-ci (dc=hudson-ci,dc=org). You’ll see this root expression repeated throughout.

The first step is to edit the /etc/openldap/ldap.conf and set the hostname and the base DN for local LDAP tools to use. In my case:

BASE dc=hudson-ci,dc=org

In a second I’ll configure the actual LDAP daemon process, but first I need to generate a  password hash to use to configure the manager or root password for the instance.  To do this you need to open a shell and run the slappasswd command:

bash$ slappasswd
New password: 
Re-enter new password: 

Copy the result (with the {SSHA} prefix) somewhere safe, we’ll need that in a moment.

Next you need to configure the LDAP daemon process through it’s configuration file slapd.conf. You’ll find a suitable example file to copy in the same /etc/openldap directory called slapd.conf.default which you should clone. In the daemon configuration file there are several sets of changes to make.

First of all, ensure that all of the required schemas are loaded. In the default sample, only the core schema will be specified, but you need to add two extras as well.

include /private/etc/openldap/schema/core.schema
include /private/etc/openldap/schema/cosine.schema
include /private/etc/openldap/schema/inetorgperson.schema

Having these schema’s loaded ensures that we can specify all of the attributes we need in our directory entries and specifically includes the uid property which is the default attribute which Hudson will use to identify a user.

Next within the slapd.conf you need to define the basic access control to attributes within the directory entries.  In this case I’ve set things up so that Hudson can authenticate a user password without itself being authenticated with the LDAP server.  Hudson does have the option to provide credentials to authenticate itself  to LDAP as we’ll see, but that will depend on how your organization’s LDAP is configured.

Here’s the access rules I have defined in this testing instance:

access to attrs=userPassword
       by self write
       by anonymous auth
       by dn.base="cn=Manager,dc=hudson-ci,dc=org" write
       by * none
access to *
       by self write
       by dn.base="cn=Manager,dc=hudson-ci,dc=org" write
       by * read

The dn.base value above refers to the LDAP manager or root user identity, imaginatively called “Manager” in my case.

Finally the core DN configuration for the Server and the manager identity used above,  and pasting in the hashed password value generated with the slappasswd command:

suffix "dc=hudson-ci,dc=org"
rootdn "cn=Manager,dc=hudson-ci,dc=org"

Apart from that, you can leave all the other defaults as they are.

Basic LDAP is now ready to run. Note that at this stage it is untuned, and probably very insecure; call in your friendly LDAP professional if you want to do anything more than just play around. The first time that you start it I recommend that you use the following command to check that the configuration is OK, the -d option will prevent it from forking and ensure that the output is visible to you as it starts up:

sudo /usr/Libexec/slapd -d Config

Now you can run a simple test using the ldapsearch command from another terminal window on the same box:

bash$ ldapsearch -x
# extended LDIF
# LDAPv3
# base <dc=hudson-ci,dc=org> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
# search result
search: 2
result: 32 No such object
# numResponses: 1

As the repository is empty the response is not very exciting.

Populating the Sample Repository

Now that LDAP is up and running we can start to create users and roles into it. The simplest way to do this is using the  ldapadd command passing it an LDIF file which defines the structure.

An example of the ldapadd command is as follows:

ldapadd -x -W -D "cn=Manager,dc=hudson-ci,dc=org" -f /Users/hudson/ldap/sampleHudsonCIRepos.ldif

The -D parameter passes the identifier of the LDAP root user  and -f specifies the ldif file to read from. When you run the command you will be prompted for the password you generated earlier and stored in hashed form in rootpw.

Here’s an excerpt from that file that sets up the initial structure:

dn: dc=hudson-ci,dc=org
objectclass: dcObject
objectclass: organization
o: Hudson Development
dc: hudson-ci
dn: ou=Roles,dc=hudson-ci,dc=org
objectClass: organizationalUnit
objectClass: top
ou: Roles

dn: ou=Committers,dc=hudson-ci,dc=org
objectClass: organizationalUnit
objectClass: top
ou: Committers

This defines the basic top level hudson-ci DN and then two organizationalUnits within that Roles and Committers. Next in the file I define several users. They all look like this with variations on actual names of course:

dn: cn=Duncan Mills,ou=Committers,dc=hudson-ci,dc=org
cn: Duncan Mills
sn: Mills
uid: duncan
userPassword: {SSHA}iVFtLeFqI84TEuDcj8VanYAtAwQMWxg9
objectClass: inetOrgPerson

You can see here that the DN of these entries includes the Committers organizationalUnit (ou=Committers) .  Each person is defined as an inetOrgPerson which means that the LDAP record can include, along with many others, the uid attribute. Again I’ve used the slappasswd command to generate a hash for this user (via userPassword) as a convenience.

Finally we have an entry for each role we want to use, in this case three roles called BuildAdminDeveloper and QAEngineer.

dn: cn=BuildAdmin,ou=Roles,dc=hudson-ci,dc=org
cn: BuildAdmin
objectclass: groupOfUniqueNames
uniqueMember: cn=Hudson Admin,ou=Committers,dc=hudson-ci,dc=org

dn: cn=Developer,ou=Roles,dc=hudson-ci,dc=org
cn: Developer
objectclass: groupOfUniqueNames
uniqueMember: cn=Winston Prakash,ou=Committers,dc=hudson-ci,dc=org
uniqueMember: cn=Bob Foster,ou=Committers,dc=hudson-ci,dc=org
uniqueMember: cn=Duncan Mills,ou=Committers,dc=hudson-ci,dc=org

dn: cn=QAEngineer,ou=Roles,dc=hudson-ci,dc=org
cn: QAEngineer
objectclass: groupOfUniqueNames
uniqueMember: cn=Latha Amujuri,ou=Committers,dc=hudson-ci,dc=org
uniqueMember: cn=Bob Foster,ou=Committers,dc=hudson-ci,dc=org

These roles specify the DN of each of their members via the uniqueMember attribute,

Once the LDIF script has been run we can use a tool such as jxplorer to view and further manage the repository (as well as using the command line tools of course)


Logging onto my local repository


And expanded view of the repository.

So my small sample LDAP repository is now all set up and ready to go.

Enabling LDAP in Hudson

If you cast your mind back to Part 1 in this series you’ll understand exactly how to do this now.  Navigate to the Hudson Security option and enable security, then select LDAP as the Security Realm:


You can see here I’ve specified a couple of bits of information other than the address of the server (which is a loopback to localhost in my case).and the Distinguished Name root for the server (dc=hudson-ci,dc=org).  The first is the User Search Filter. This is filtering on the uid value that I defined with each record. Hence, to login to the server I’ll need to use my uid identity (duncan) as my username.  Note that I could equally filter by some other LDAP attribute here such as email address.  Secondly I’ve specified a Group Search Base.  Now when Hudson is tying to match a role / group it knows to start the search in the LDAP hierachy at ou=Roles,dc=hudson-ci,dc=org.

Because of the access control rules that I specified in the slapd.config, there is no need for me to specify Manager credentials to connect to the repository.

Using Teams with LDAP

At this stage, if you’ve been following along, you should be able to log in as one of the users defined in your LDAP repository although we’ve not enabled any authorization yet (anyone can do anything).  As in Part 1, I’ll go in and select Team based Authorization Strategy in the security Authorization section of the security page, and define a Super-Admin. In this case, rather than directly specifying the Hudson Admin user (admin) that I defined in LDAP I can use the Role that Admin belongs to – “BuildAdmin”.

To do this you convert the LDAP role name to all uppercase and prefix it with the string “ROLE_”. Thus, when prompted for the User or Group name for the System Admin I can supply the value “ROLE_BUILDADMIN”.


Saving that, I can now log in to Hudson using the uid “admin” which matches the Hudson Admin user and it’s LDAP userPassword. Because this user is allocated to the BuildAdmin group I’ll now be the Hudson team super-admin.

In the same way I can define the same teams as before (see Part 1) , Dev and QA, but this time, rather than adding individuals as members I can use the ‘ROLE_DEVELOPER” and “ROLE_QAENGINEER” values respectively, automatically enrolling the members of those roles into the relevant teams.

Note that using roles mapping is not an all-or-nothing thing.  You are able to mix and match roles and users, so for example you may choose to explicitly nominate a team admin as a real user, but then the bulk of the team membership comes from the role(s), e.g.


So if I now log in to Hudson as duncan (a member of the Developer group/role) here is the list of jobs that I see:


I can see the read-only public job “Job1”  and the Dev team specific job “Dev.BuildHudson” because the Developer role is assigned as a member of the Dev team in Hudson.

Of course, now that core membership of teams is defined through LDAP you no longer have to manage membership changes through Hudson. As a new developer joins your org and is enrolled in your corporate  LDAP groups then they will automatically be able to access the relevant resources in Hudson – a great leap forward!

Although I’ve used LDAP to illustrate using simplified team management in Hudson, the same approach will apply with the other Security Realm types – Unix Users /Groups and the roles assigned to an authenticated user in your Java EE Servlet container.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: