My ShipIt project this time was fairly ambitious: provide a pluggable user repository layer for Confluence that allows dynamic configuration of repositories for users, groups and properties associated with these. (Whew! That was a mouthful.) In essence what it means is while Confluence can retrieve users and groups from a few different places at the moment, we’d like to make it easier to use and extend this functionality.
Taking a look across the user management landscape of Confluence, there are two user management libraries each with a variety of configurations:
- OpenSymphony User (OSUser).This is the default user and group management for Confluence, and it works well for many scenarios. It can use any of the following configurations:
- Hibernate (database) configuration
- Hibernate configuration plus LDAP authenticator
- JIRA database configuration
- Atlassian-User. This is our new user management library. Compared to OSUser, it supports more repositories and is better with large numbers of users and groups. It supports a combination of any of the following configurations:
- Hibernate (database) repository
- LDAP repository
- Crowd repository
Configuring these currently is a minefield of obscure XML configuration files, restarting the application, and crossing your fingers and hoping it works. Not easy, and definitely not up to the standard we aim for with administering our applications.
Configuring Atlassian-User via XML
My ShipIt project was to make all this go away. Hiding unnecessary complexity is the goal, and I was going to achieve it with only a few changes to Confluence:
- Assume only Atlassian-User exists. (We’ll be there in a few releases – I promise!)
- Make Atlassian-User repositories configurable while Confluence is running.
- Write an admin screen, Manage user repositories, that allows the repositories to be configured while Confluence is running.
- Make Atlassian-User repository types into user repository type plugins. So, for example, LDAP support is a plugin.
- Create an LDAP user-repository-type plugin, and allow it to be configured while Confluence is running.
- In the LDAP configuration screens, allow the user to test their configuration before saving it.
I managed to get all these done, for some definition of done. They’re not tested, but they work well enough for a demo. I’ll now run through each part in turn.
1. Assume only Atlassian-User exists
Before starting Confluence for the first time, change the default repository in atlassian-user.xml from OSUser to Atlassian-User Hibernate. Set up Confluence.
2. Make Atlassian-User repositories configurable while Confluence is running
All Confluence calls to the user management system go through a single interface, called the UserAccessor. I wrote a DynamicUserAccessor which, in essence, stores a list of repositories in order. The repositories can be added, removed and reordered while Confluence is running.
(Nitpickers can forget for a moment about the transactional effects this has. We’re in single-user mode for our ShipIt work.)
3. Write an admin screen that allows the repositories to be configured
It looks like this:
Under the hood, it just updates the list that lies behind the DynamicUserAccessor. Reordering the repositories is supported. Reconfiguring the existing repositories is supported.
You need to be careful, however, because changing the order of the repositories can affect your own ability to access the application. I think we need to work out a way to prevent the user shooting himself in the foot, perhaps by having a fallback username and password that must be used to configure these settings.
4. Create user-repository-type plugins
We currently have four different repository types provided with Atlassian-User by default, and several companies have written their own. It makes sense to install these as a plugin in Confluence, which can be enabled, disabled and upgraded independently of the application.
Fortunately, writing a new plugin type for Confluence isn’t hard. I did the following:
- wrote a new module descriptor class, UserRepositoryTypeModuleDescriptor
- registered the class with the plugin manager, corresponding to the ‘user-repository-type’ XML tag in atlassian-plugin.xml
- modified the user repository admin screen to get a list of available repository types from the plugin manager.
A user-repository-type plugin is very simple. In fact it contains only three bits of information:
- a URL to create a new repository
- a URL to configure an existing repository
- the class name of the repository implementation.
5. Create an LDAP user-repository-type plugin
Creating an LDAP plugin was the most complex part of the ShipIt project. The plugin needed to be able to configure an LDAP repository dynamically, retrieve users and groups and perform searches.
The LDAP repository included in Atlassian-User is tricky to configure dynamically. It can really only read an XML file at the moment. So instead of struggling with that, I extended and enhanced the LDAP library which is part of our LDAP testing tool, Paddle.
The screen to create a new LDAP repository looks like this:
Saving this repository adds a new user management repository to Confluence immediately, and lets you grant permission to LDAP users so they can access Confluence straight away. There is no need to restart the application or modify any XML configuration.
6. Allow the user to test their configuration before saving it
Another big drawback with configuring LDAP in Confluence at the moment is that there’s no way of testing your configuration short of starting up the application to see if it works. With a dynamically configurable repository like this, I figured it would be really nice to implement a testing mechanism so the administrator could check that the settings were working before saving them.
After each section in the configuration screen is a test button. These are to let you test:
- Confluence can connect to your LDAP server
- your username and password are correct
- your LDAP user settings can be used to locate users in the LDAP tree
- your LDAP group settings can be used to locate groups in the LDAP tree
- your LDAP membership settings can be used to identify at least one group membership
As well as success or failure, the test also includes the server’s response time for all the above operations. This can help identify performance problems with the server or with the LDAP configuration provided, before they affect the application.
Here’s what you’ll see if your user test fails to find any users:
The tests also make an attempt to convert common LDAP failure messages into meaningful messages for the user. For example, rather than “Code 49: authentication failure”, an incorrect username or password will show the following message:
A successful LDAP settings test shows a green message to the user:
Group search test successful
The outcome of this ShipIt project was better than I ever imagined. It showed a new way of configuring user repositories in Confluence that would provide significant benefits to our users, as well as reducing support work around configuring the difficult LDAP settings.
For the last twelve months, it has been one of my technical goals to push towards a user management system like this; now I have a more concrete understanding of what it is I want to achieve and how it can be done.
To integrate this into Confluence, a few prerequisites need to be taken care of:
- we need to migrate all users from OSUser to Atlassian-User via an upgrade task
- we need to improve the API of Atlassian-User to support simple creation of new repositories independent of XML configuration
- we need a well-designed LDAP repository, which is capable of being dynamically configured and extensible.
This ShipIt project was a glimpse into the future, showing how I think user repositories should be managed within Confluence. Unfortunately, we’ll need to do a bit of work before we can bring this into Confluence for real, but it should be possible if we can prioritise this work within the next six to twelve months.