diff app/models/auth_source_ldap.rb @ 1338:25603efa57b5

Merge from live branch
author Chris Cannam
date Thu, 20 Jun 2013 13:14:14 +0100
parents 433d4f72a19b
children 622f24f53b42
line wrap: on
line diff
--- a/app/models/auth_source_ldap.rb	Wed Jan 23 13:11:25 2013 +0000
+++ b/app/models/auth_source_ldap.rb	Thu Jun 20 13:14:14 2013 +0100
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2011  Jean-Philippe Lang
+# Copyright (C) 2006-2012  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -15,40 +15,49 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
+require 'iconv'
 require 'net/ldap'
-require 'iconv'
+require 'net/ldap/dn'
+require 'timeout'
 
 class AuthSourceLdap < AuthSource
   validates_presence_of :host, :port, :attr_login
   validates_length_of :name, :host, :maximum => 60, :allow_nil => true
-  validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
+  validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
   validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
   validates_numericality_of :port, :only_integer => true
+  validates_numericality_of :timeout, :only_integer => true, :allow_blank => true
+  validate :validate_filter
 
   before_validation :strip_ldap_attributes
 
-  def after_initialize
+  def initialize(attributes=nil, *args)
+    super
     self.port = 389 if self.port == 0
   end
 
   def authenticate(login, password)
     return nil if login.blank? || password.blank?
-    attrs = get_user_dn(login)
 
-    if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
-      logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
-      return attrs.except(:dn)
+    with_timeout do
+      attrs = get_user_dn(login, password)
+      if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
+        logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
+        return attrs.except(:dn)
+      end
     end
-  rescue  Net::LDAP::LdapError => text
-    raise "LdapError: " + text
+  rescue Net::LDAP::LdapError => e
+    raise AuthSourceException.new(e.message)
   end
 
   # test the connection to the LDAP
   def test_connection
-    ldap_con = initialize_ldap_con(self.account, self.account_password)
-    ldap_con.open { }
-  rescue  Net::LDAP::LdapError => text
-    raise "LdapError: " + text
+    with_timeout do
+      ldap_con = initialize_ldap_con(self.account, self.account_password)
+      ldap_con.open { }
+    end
+  rescue Net::LDAP::LdapError => e
+    raise AuthSourceException.new(e.message)
   end
 
   def auth_method_name
@@ -57,6 +66,30 @@
 
   private
 
+  def with_timeout(&block)
+    timeout = self.timeout
+    timeout = 20 unless timeout && timeout > 0
+    Timeout.timeout(timeout) do
+      return yield
+    end
+  rescue Timeout::Error => e
+    raise AuthSourceTimeoutException.new(e.message)
+  end
+
+  def ldap_filter
+    if filter.present?
+      Net::LDAP::Filter.construct(filter)
+    end
+  rescue Net::LDAP::LdapError
+    nil
+  end
+
+  def validate_filter
+    if filter.present? && ldap_filter.nil?
+      errors.add(:filter, :invalid)
+    end
+  end
+
   def strip_ldap_attributes
     [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
       write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
@@ -100,14 +133,24 @@
   end
 
   # Get the user's dn and any attributes for them, given their login
-  def get_user_dn(login)
-    ldap_con = initialize_ldap_con(self.account, self.account_password)
+  def get_user_dn(login, password)
+    ldap_con = nil
+    if self.account && self.account.include?("$login")
+      ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password)
+    else
+      ldap_con = initialize_ldap_con(self.account, self.account_password)
+    end
     login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
     object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
     attrs = {}
 
+    search_filter = object_filter & login_filter
+    if f = ldap_filter
+      search_filter = search_filter & f
+    end
+
     ldap_con.search( :base => self.base_dn,
-                     :filter => object_filter & login_filter,
+                     :filter => search_filter,
                      :attributes=> search_attributes) do |entry|
 
       if onthefly_register?