Name

Crowd as an LDAP Server

Version

1.0

Product Versions

Crowd v1.0.6

Author(s)

Robert Castaneda [CODEGEIST:CustomWare] , Daren Klamer [CODEGEIST:CustomWare]

Homepage

http://www.customware.net

Price

FREE! Priceless!

License

N/A

JavaDocs

 

IssueTracking

 

Download JAR

 

Download Source

Code, what code? This one is about re-use!

Description/Features

A layer to turn Crowd into an LDAP server

Motivation - a non-Atlassian product to integrate into our environment. The product supports LDAP authentication, we want to use our Crowd deployment as the central repository of users, rather than setting up an LDAP repository just for this application.

A picture is a thousand words - here is the end result in JXplorer of users listed from Crowd via LDAP:

The requirement is to have no Java/J2EE/Atlassian based products use crowd as the central authentication mechanism via LDAP. To achieve this, we have used Penrose - http://penrose.safehaus.org/ and have configured it to read user/password information from the Crowd database as well as implement a password decrypt/decode function in Penrose so that it can authenticate using the same passwords that are stored in Crowd without duplicating user information. A user in Crowd, is a user in LDAP. The diagram shows the setup below:

Crowd LDAP

Whilst not a plugin that runs within an Atlassian product, it's another solution that can bring outside technologies into the Atlassian world.

How an external application authenticates over LDAP can be different from one application to another. This example has been tested using Crowd itself, that is , using the Penrose LDAP server as a Crowd Directory service and a Crowd external Application setup to test against this directory.

At any time, Crowd can update their algorithm for storing passwords. The mechanism described here makes us of the fact that having access to the right data, a password can be reverse engineered/constructed from the Crowd database. If this ability changes in future, then this will need to be updated, probably to use the

Unknown macro: {SHA-512}

<pwd> type syntax.

As always, an application/infrastructure such as Crowd is only as secure as the infrastructure -database/os - that it runs on.

Steps to setup

  • We then create a Source. In Crowd, we created a View rather than accessing the tables directly. The main table that you need access to is REMOTEPRINCIPALCREDENTIALS

  • We then and add a New Dynamic Entry, not a "Dynamic Entry from Source". Within that entry definition, we define the source within the dynamic entry.

Code

The following code decrypts passwords stored in Crowd, note that you need to retrieve the RETRIEVE_ME_FROM_YOUR_CROWD_DATABASE parameter from your DB. To retrieve this parameter, execute the following SQL from the Crowd database:

select * from "SERVERPROPERTY" where "NAME" = 16

The code is below.


import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

            // Buffer used to transport the bytes from one stream to another
        byte[] buf = new byte[CODEGEIST:1024];

        try {
            byte[] hash = new BASE64Decoder().decodeBuffer( RETRIEVE_ME_FROM_YOUR_CROWD_DATABASE );
            SecretKey key = new SecretKeySpec( hash, "DES" );

            DESKeySpec paramSpec = new DESKeySpec( hash );

            Cipher dcipher = Cipher.getInstance( "DES" );
            dcipher.init( Cipher.DECRYPT_MODE, key );

            ByteArrayInputStream bis = new ByteArrayInputStream( new BASE64Decoder().decodeBuffer( thepwd ) );

            InputStream in = new CipherInputStream( bis, dcipher );

            // Read in the decrypted bytes and write the cleartext to out
            int numRead = 0;

            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            while( ( numRead = in.read( buf ) ) >= 0 ) {
                bos.write( buf, 0, numRead );
            }
            return new String( bos.toByteArray() ) ;
        } catch( Exception e ) {
            e.printStackTrace();
        }

Screenshots

6 Comments

  1. What timing... I was just getting ready to use Penrose for an app that does not have a crowd adapter...

    Now I can use Crowd (smile)    which will make my life much easier...

    I have followed the directions ... as minimal as they are (wink)   ...  And I can not get any users to return I get "Search "cn=users,ou=Users,dc=example,dc=com" with scope base and filter "(objectClass=*)
    [05/14/2007 14:43:23] Search operation succeded.
    [05/14/2007 14:43:23] Search operation returned 0 entries.
    " every time I try to look at the users via JXplorer...

    Should the userPassword code be entered as text or as an expression?  Any suggestions for further debugging?

    Thanks,

    Collin

    1. Hi Colin,

      Thanks for trying this out. The password piece is only required if you want to authenticate against LDAP, not browse it. When you craete your source in Penrose Studio, are you able to see the list of results within Penrose Studio itself? this is the first step that needs to be verified before trying to access it externally.

  2. The SQL for the view is...

    CREATE OR REPLACE VIEW public.crowd_view AS
     SELECT a."REMOTEPRINCIPALNAME" AS principal_name
    , a."CREDENTIAL" AS the_credential
    , to_char(b."CONCEPTION", 'YYYYMMDDHH24MISSMS'::character varying) AS the_uid FROM 
    "REMOTEPRINCIPALCREDENTIALS" a, "REMOTEPRINCIPAL" b WHERE ((a."REMOTEPRINCIPALNAME")::text = (b."NAME")::text);
    
    
  3. Is there any way to get crowd groups linked to the users exposed through ldap? We want to tie into LDAP and auth based on what group a user is in. Etc...

    I'm sure it's possible, but the penrose docs are pretty lacking imho.

    Thanks for any help!

  4. Hi,

    thanks for the nice extension (smile) Before trying it out thought, I would like to understand if only users configured internally in Crowd are exposed (which I assume, since it's reading the user information from the Crowd database), or if also directories, that are connected to Crowd are then immediatly accessible?

    I assume that other directories, that are connected to crowd, should be connected to penrose as well, so that you have all users merged in one LDAP.

    Andreas

  5. Is there any way to get this to work with crowd 2, it looks like the schema has changed?

    Cheers

    Lee