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
Advertising bad habits. 😀 You have your feature and implementation wrong way around, you know. If you wanna be talking about BDD, you should always first describe the feature, see it fail (this is to test the test) and then write (just enough) code to make it pass.
Thank you for your comment. You are right, I will try to be more specific next time 😉