I have accustomed to using Cucumber with Ruby on Rails for Behavior Driven Development when using sql database backends. What if data was stored within LDAP? I researched this point and it was very easy.

I assume that you have installed Ubuntu 10.04 (Lucid) and have configured the OpenLDAP server with the following instructions (Server setup): Setting up OpenLDAP on Ubuntu 10.04 Alpha 2 (Lucid)

We have to add some access rules to slapd configurations so that ActiveLdap will work. This is not necessarily the right way to do this but with these simple changes we can make ActiveLdap work. Don’t do this to the production server if you don’t know what it means.

modify_acls.ldif:

dn: olcDatabase={-1}frontend,cn=config
add: olcAccess
olcAccess: {1}to dn.base=cn=subschema by * read
olcAccess: {2}to dn.base= by * read
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f modify_acls.ldif

Now we can start to make preparations for the new Rails project. In this example I use Ubuntu 10.04 Alpha 3 (Lucid)

Install the following packages from Ubuntu repositories:

sudo apt-get install ruby rubygems rake ruby-dev irb libxml2-dev libxslt-dev libopenssl-ruby

The following gems are needed:

sudo gem install rails cucumber-rails webrat activeldap ruby-net-ldap

Add the gem binaries to PATH:

echo "PATH=$PATH:/var/lib/gems/1.8/bin" >> ~/.bashrc

Create new rails project:

rails user-management

As the next step, generate new REST resources with scaffold. This command also performs database migration. This is good because then we can verify that the Cucumber step passed correctly in the typical use case (using an sqlite database). We use the posixAccount objectClass (LDAP) attribute name. Thus we don’t have to rename the attribute as we introduce ActiveLdap.

./script/generate scaffold User cn:string uid:string uidNumber:integer gidNumber:integer 
homeDirectory:string

Create tables to the test database:

RAILS_ENV=test rake db:migrate

Cucumber initialization:

./script/generate cucumber
echo "default: --format pretty features" >> cucumber.yml

Generate User feature:

./script/generate feature User cn:string uid:string uidNumber:integer gidNumber:integer 
homeDirectory:string

Edit manage_users.feature file and replace the following content:

features/manage_users.feature:

Feature: Manage users
  In order to manage authorization
  User
  wants to add, remove, and organize users

  Scenario: Add new user
    Given I am on the new user page
    When I fill in "Cn" with "Joe Taylor"
    And I fill in "Uid" with "joe"
    And I fill in "Uidnumber" with "11001"
    And I fill in "Gidnumber" with "11001"
    And I fill in "Homedirectory" with "/home/joe"
    And I press "Create"
    Then I should see "Joe Taylor"
    And I should see "joe"
    And I should see "11001"
    And I should see "11001"
    And I should see "/home/joe"

Now you can run Cucumber

cucumber

..and the result should be one passed scenario.

Now we have User resource and we can test that it works. Data was stored in the sqlite database and in the next step we switch to ActiveLdap and LDAP storage.

Add following line to config/environment.rb:

config.gem 'activeldap', :lib => 'active_ldap'

Initialize ActiveLdap:

./script/generate scaffold_active_ldap

Edit config/ldap.yml file and add the following content:

cucumber:
  host: localhost
  base: dc=edu,dc=example,dc=org
  bind_dn: uid=admin,ou=People,dc=edu,dc=example,dc=org
  password: example

Modify app/models/user.rb file and replace it with the following content:

class User < ActiveLdap::Base
  ldap_mapping( :dn_attribute => "uid",
                :prefix => "ou=People",
                :classes => ['top', 'posixAccount', 'account'] )
end

OpenLDAP does not support a transaction system such as in a typical SQL-database. This is why we need use Before hook. First we have to clear the data so that the test scenario works correctly.

Edit features/step_definitions/user_steps.rb file and add following content:

Before do |scenario|
  User.all.each do |u|
    u.destroy
  end
end

It should now work. Run cucumber command:

cucumber

..and the result should be one passed scenario.

Add the following LDAP specific scenario. Edit features/manage_users.feature file and add the following content:

  Scenario: Add dupplicate distinguishedName
    Given the following users:
    | cn         | Uid | Uidnumber | Gidnumber | Homedirectory |
    | Joe Taylor | joe |     11001 |     11001 | /home/joe     |
    And I am on the new user page
    When I fill in "Cn" with "Joe Wilson"
    And I fill in "Uid" with "joe"
    And I fill in "Uidnumber" with "11002"
    And I fill in "Gidnumber" with "11001"
    And I fill in "Homedirectory" with "/home/joew"
    And I press "Create"
    Then I should see "distinguishedName is duplicated"
    When I fill in "Uid" with "joew"
    And I press "Create"
    Then I should see "Joe Wilson"
    And I should see "joew"
    And I should see "11002"
    And I should see "11001"
    And I should see "/home/joew"

DN duplicates are not allowed on the LDAP-server. This scenario ensures that the LDAP-server behaves like this.

Run cucumber command.

Result should be following:

2 scenarios (2 passed)
27 steps (27 passed)

Jouni Korhonen