LDAP authentication with merb-auth
At work, I have several web apps that our staff need admin access to, all of which use the same LDAP database. This allows them to change their password in one place for all our systems.
It also means that I have to integrate LDAP authentication into every web app I write. So yesterday, I spent the afternoon making merb-auth talk to our LDAP server. Once I wrapped my head around how merb-auth works, it turned out to be pretty easy.
<span id="more-976"></span>
<p>I already had an app based on merb 0.9.2 and merbful_authentication, so I already had a bit of glue code to authenticate a user against the LDAP server, based on <a href="http://rubyforge.org/projects/net-ldap/">ruby-net-ldap</a>. This is in Merb.root/lib/auth_ldap.rb:</p>
require 'net/ldap'
require 'yaml'
module Auth
module LDAP
CONNECTION_KEYS = [:host, :port, :auth, :encryption]
def self.settings
@settings ||=
YAML.load_file(File.join(Merb.root, 'config', 'ldap.yml'))
end
def self.create_connection!
@ldap = Net::LDAP.new({ :port => 389, :auth => {:method => :simple},
:encryption => :simple_tls
}.merge(settings[:connection]))
end
def self.authenticate(login, password)
create_connection!
@ldap.auth("#{settings[:login_attr]}=#{login},#{settings[:user_base]}",
password)
@ldap.bind # try to authenticate as the given user
end
def self.get_user(login, password)
user = nil
if authenticate(login, password)
@ldap.search(:base => settings[:user_base],
:filter => Net::LDAP::Filter.eq(settings[:login_attr],
login)) do |entry|
user = entry.uid
end
end
user
end
end
end
This reads its LDAP configuration from where you’d expect: Merb.root/config/ldap.yml, which looks something like this:
---
:connection:
:host: ldap.example.com
:port: 636
:user_base: ou=People,dc=example,dc=com
:login_attr: uid
The :connection settings are passed directly to Net::LDAP, so the configuration options supported are :host, :port, :auth, and :encryption. See the Net::LDAP RDoc documentation for more details.
Then I of course had to set up the User model. Here are the salient bits:
class User < ActiveRecord::Base
# Class methods
class << self
# Authenticates a user by their login name and unencrypted password.
# Returns the user or nil.
def authenticate(login, password)
u = find_by_login(login)
u && u.authenticated?(password) ? u : nil
end
end
def authenticated?(password)
Auth::LDAP.authenticate(login, password)
end
# virtual attribute used in on-the-fly user creation from LDAP
attr_accessor :password
# ... other methods, validations, etc. ...
end
I had to remove the code from Merb.root/merb/merb-auth/setup.rb that includes the SaltedUser mixin from merb-auth-more into the User model automatically—no need for salt since we’re not storing the passwords at all!
The LDAP auth code and the User model being in place, I enabled the merb-auth slice (by adding slice :merb_auth_slice_password to the beginning of my routes), et voilà! LDAP-backed password authentication.
No comments
Jump to comment form | comments rss [?] | trackback uri [?]