LDAP Picker Custom Field

I needed a custom field whereby a user could pick a sponsor for an enhancement… a sponsor may be a jira user, but may well not be – eg. business users are not all jira users. I wanted a field that worked in the same way as the funky new AJAX user picker in jira – but instead queried the corporate LDAP directory.

imgaf.jpg

Sorry about all the blurring here – trust me that the department and sub-departments are shown. The results can show an arbitrary string, eg Fullname, username, department, location and so on. Whatever helps your users in locating the right entry. It can also return any LDAP attribute – username is probably most sensible, but could be full name, department, email address or anything else.

Development

In developing this I followed my standard development methodology, which is create copies of classes and javascript that are close to what I want, then gradually alter them until they do my bidding. In this case I started with com.atlassian.jira.web.dwr.AjaxUserPicker, renaming it to LDAPUserPicker. Although this modus operandi normally works for me, I regret in this instance not reading around the subject a little and having some sort of plan before hacking the code.

I spent a long time trying to find AjaxUserPicker.js, in fact this is specified using the <web-resource> tag, but DWR actually generates this javascript function so the AjaxUserPicker class’s methods can be called. I think I could have done this a lot quicker if I had read up a bit on DWR (retrieves the results) and YUI (respins to JS events and draws the drop-down etc) which are the technologies Atlassian has built on. Here is an example of a YUI autocomplete control.

If you should want to do something similar I’d offer a few tips…

You can enable a DWR test page, where you can test your java methods by editing web.xml – change the false to true, so it looks like:

loading http://blogs.onresolve.com/jechlin/30/web.xml…

Now you should be able to hit a page called http://<serverroot>/dwr/index.html. This will show you all the classes that DWR knows about (see dwr.xml), example:

imgBC.jpg

Great, so that’s working. It helps to know what params getUsers expects – see the definition in AjaxUserPicker.java:

public AutoCompleteResults getUsers(String fieldName, String query)

When you hit Execute the results are shown in a popup.

My next tip is to enable debugging of javascript. Edit atlassian-jira\includes\js\widget\autocomplete-widget.js.

loading http://blogs.onresolve.com/jechlin/30/enable-logging.js…

Right near the top, comment out the “if (true) return” line. This will now give you a log function that you can stick in the javascript:

atlassian.jira.widget.log('Some text here', 'info', this.toString());

Reload the page and…

imgA8.jpg

For example, I put this log function in the dwrInvokerFunc which is initialised for each field that has the DWR functionality, 7 in this case. There is also a timer so you can check performance.

What else… if you have set everything else up right, using the “debugger;” statement in your javascript will allow you to open the JS in your debugger, that can be helpful.

Fiddler is one of those tools that once you’ve got used to you winer how you ever lived without. Prior to this I was using the HTTP Inspector in Perl Dev Kit, and Wireshark… neither are remotely as useful for debugging web apps. Anyway, I used Fiddler to look at the contents of the request and responses, to try and work out why the LDAP picker was not working.

Installation

is slightly complex.

  1. Copy the jar to atlassian-jira/WEB-INF/lib. Source here.
  2. Edit atlassian-jira\WEB-INF\classes\system-webresources-plugin.xml. Find key=”ajax-userpicker”, then after it add:
  3. Edit atlassian-jira\WEB-INF\dwr.xml. Add:
  4. Copy LDAP_CFPicker.properties to atlassian-jira/WEB-INF/classes.
  5. Restart jira… then configure. (BTW I’ve tested on Windows XP and Solaris using Jira 3.11).

If anyone knows how this can be done without altering either of these XML files let me know – I don’t think it can be done entirely within the plugin.

If you need to convert an existing field, say Sponsor, to an LDAP picker you can use this SQL (then restart, reindex):

loading http://blogs.onresolve.com/jechlin/30/cfsql.sql…

Configuration

is a little complex we well. First of all add a new custom, you should see a new CF type LDAP Picker if installation worked.

imgC9.jpg

Each field configuration can be configured to have a different search query and so on… Go to the field you just created and do Configure -> Edit Configuration. You will have a URL like the following. Note the fieldConfigSchemeId in the URL:

imgCD.jpg

So for each configuration of your custom field, you will need the following options in atlassian-jira/webapp/WEB-INF/classes/LDAP_CFPicker.properties. Replace 10240 with whatever fieldConfigSchemeId you see in your address bar.

The following example will run a query against the sAMAccountName, using whatever the user has typed in to the Business Sponsor custom field:

10210.searchbase = DC=acme,DC=org
10210.queryFmt=(&(objectClass=user)(sAMAccountName={0}*))
10210.returningAttributes=cn,sAMAccountName,department
10210.displayFormat={0} – ({1}) <i>{2}</i>
10210.returnAttr=sAMAccountName

So each attribute is prefixed with the field config scheme ID. In a bit more detail:

searchbase is your base DN for this query.
queryFmt is the query that will be run when the user types a character in the search field. This is a MessageFormat, so in the example above, if I typed in the characters “echl”, the LDAP query would be (&(objectClass=user)(sAMAccountName=echl*)).
returningAttributes is the list of LDAP attributes to return, these will be used in the displayFormat below
displayFormat is what will be display in the dropdown. Use HTML if you like. Each attribute will be bolded with the matching part of the search string
returnAttr is the attribute which will appear in the field when the user selects one of the items in the dropdown

http://confluence.atlassian.com/display/DEV/How+to+write+a+LDAP+search+filter may be useful.

There are a several example usages in the .properties file:

  • Pick a user querying on login name
  • Pick a user that is a member of a group
  • Pick a user querying on and returning their common name
  • Search from a list of groups a user is a member of

TODO

There is no validation (which happens to suit me), however, I don’t believe validation of custom fields is possible.

The main TODO is probably configuration – I tried to add a webwork action so that you could specify the config in the configuration of the custom field. But gave up in the end… could not find something close enough to what I needed.

The searcher is just the plain text searcher – it may be preferable to use the same picker for the searcher.

9 comments to LDAP Picker Custom Field

  • This post is a great “how to” for people wanting to extend Jira in general. The tips that particularly struck me were:

    - get a basic page working first, possibly copied from an existing one, but not always
    - make incremental changes, and make the initial effort to set up your development environment so that you can see the results with as little effort as possible

    Also, be careful you don’t update an existing field type and searcher without checking that the underlying custom field types match. If you go from int to string you have to copy the data in the underlying fields. (This bit me last week.)

    ~Matt

  • Great stuff agaon (I have bookmarked this blog.. ;-).

    Rg. Fiddler, I must say that I found it a little bit awkward to use it (about 1.5 years ago). Is version 2.0 better?

    -Stefan

  • Another thought: what’s the performance like with the LDAP lookup for 10,000 users?

  • Hi Matt,

    Good question. It really depends on the format of the query you run. This type of query:

    (&(objectClass=user)(sAMAccountName={0}*)(department=*))

    is fine, I have tested this on a site with upwards of 50k objects of objectClass=user, no problem. That after all is what ldap databases are designed for.

    On the other hand, this:

    sAMAccountName=*{0}*

    ie, search the string within the name, is slow…

    jamie

  • Hi Stefan,

    I’ve not used 1.5 but 2.0 seems easy enough to use. But to be honest I on;y really use the Session Inspector tab.

    cheers, jamie

  • ssenecal

    You can put the DWR configuration stuff in the plugin JAR. Take a look at the sample app posted here: http://forums.atlassian.com/thread.jspa?threadID=34821&tstart=0

  • roberto.manicardi

    Hi,
    first of all thank you for your useful post!

    I couldn’t in enabling the javascript logging and I found why.

    The problem was that, by default, to save bandwith Jira load the autocomplete-min.js script.

    To load the full version of the same script (autocomplete.js), you should change the yui-autocomplete web-resource in the atlassian-jira\WEB-INF\classes\system-webresources-plugin.xml as following:

    Remember to reload Jira and to clear the browser cache.

    HTH,
    Roberto

  • Dieter Greiner

    Hi Jamie,

    Thank’s a lot for this great contribution to JIRA!

    Do you think it’s possible to use your LDAP picker field to enter a comma separated list of user ids ? (i mean like the JIRA standard multi user picker custom field)

    Cheers,
    Dieter

  • Dieter Greiner

    Hi Jamie,

    I have modified AjaxLDAPPicker.java a little bit so one can also use the configuration scheme label name instead of the configuration id in LDAP_CFPicker.properties. If you find it useful i’d be glad if you can integrate this in your version.

    The enhancement is here: http://www.copypastecode.com/43365

    Cheers,
    Dieter

Leave a Reply