File: app/models/auth_source_ldap.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  class: AuthSourceLdap#22
inherits from
  AuthSource   
has properties
class method: human_attribute_name / 2 #32
method: initialize / 2 #40
method: authenticate / 2 #45
method: test_connection #58
method: auth_method_name #65
method: ldap_filter #71
method: validate_filter #79
method: strip_ldap_attributes #85
method: initialize_ldap_con / 2 #91
method: get_user_attributes_from_ldap_entry / 1 #100
method: search_attributes #112
method: authenticate_dn / 2 #121
method: get_user_dn / 2 #128
class method: get_attr / 2 #160

Class Hierarchy

Object ( Builtin-Module )
Base ( ActiveRecord )
AuthSource
  AuthSourceLdap    #22

Code

   1  # Redmine - project management software
   2  # Copyright (C) 2006-2011  Jean-Philippe Lang
   3  #
   4  # This program is free software; you can redistribute it and/or
   5  # modify it under the terms of the GNU General Public License
   6  # as published by the Free Software Foundation; either version 2
   7  # of the License, or (at your option) any later version.
   8  #
   9  # This program is distributed in the hope that it will be useful,
  10  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  # GNU General Public License for more details.
  13  #
  14  # You should have received a copy of the GNU General Public License
  15  # along with this program; if not, write to the Free Software
  16  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17 
  18  require 'iconv'
  19  require 'net/ldap'
  20  require 'net/ldap/dn'
  21 
  22  class AuthSourceLdap < AuthSource
  23    validates_presence_of :host, :port, :attr_login
  24    validates_length_of :name, :host, :maximum => 60, :allow_nil => true
  25    validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
  26    validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
  27    validates_numericality_of :port, :only_integer => true
  28    validate :validate_filter
  29 
  30    before_validation :strip_ldap_attributes
  31 
  32    def self.human_attribute_name(attribute_key_name, *args)
  33      attr_name = attribute_key_name.to_s
  34      if attr_name == "filter"
  35        attr_name = "ldap_filter"
  36      end
  37      super(attr_name, *args)
  38    end
  39 
  40    def initialize(attributes=nil, *args)
  41      super
  42      self.port = 389 if self.port == 0
  43    end
  44 
  45    def authenticate(login, password)
  46      return nil if login.blank? || password.blank?
  47      attrs = get_user_dn(login, password)
  48 
  49      if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
  50        logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
  51        return attrs.except(:dn)
  52      end
  53    rescue  Net::LDAP::LdapError => e
  54      raise AuthSourceException.new(e.message)
  55    end
  56 
  57    # test the connection to the LDAP
  58    def test_connection
  59      ldap_con = initialize_ldap_con(self.account, self.account_password)
  60      ldap_con.open { }
  61    rescue  Net::LDAP::LdapError => e
  62      raise "LdapError: " + e.message
  63    end
  64 
  65    def auth_method_name
  66      "LDAP"
  67    end
  68 
  69    private
  70 
  71    def ldap_filter
  72      if filter.present?
  73        Net::LDAP::Filter.construct(filter)
  74      end
  75    rescue Net::LDAP::LdapError
  76      nil
  77    end
  78 
  79    def validate_filter
  80      if filter.present? && ldap_filter.nil?
  81        errors.add(:filter, :invalid)
  82      end
  83    end
  84 
  85    def strip_ldap_attributes
  86      [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
  87        write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
  88      end
  89    end
  90 
  91    def initialize_ldap_con(ldap_user, ldap_password)
  92      options = { :host => self.host,
  93                  :port => self.port,
  94                  :encryption => (self.tls ? :simple_tls : nil)
  95                }
  96      options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
  97      Net::LDAP.new options
  98    end
  99 
 100    def get_user_attributes_from_ldap_entry(entry)
 101      {
 102       :dn => entry.dn,
 103       :firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
 104       :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
 105       :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
 106       :auth_source_id => self.id
 107      }
 108    end
 109 
 110    # Return the attributes needed for the LDAP search.  It will only
 111    # include the user attributes if on-the-fly registration is enabled
 112    def search_attributes
 113      if onthefly_register?
 114        ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]
 115      else
 116        ['dn']
 117      end
 118    end
 119 
 120    # Check if a DN (user record) authenticates with the password
 121    def authenticate_dn(dn, password)
 122      if dn.present? && password.present?
 123        initialize_ldap_con(dn, password).bind
 124      end
 125    end
 126 
 127    # Get the user's dn and any attributes for them, given their login
 128    def get_user_dn(login, password)
 129      ldap_con = nil
 130      if self.account && self.account.include?("$login")
 131        ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password)
 132      else
 133        ldap_con = initialize_ldap_con(self.account, self.account_password)
 134      end
 135      login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
 136      object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
 137      attrs = {}
 138 
 139      search_filter = object_filter & login_filter
 140      if f = ldap_filter
 141        search_filter = search_filter & f
 142      end
 143 
 144      ldap_con.search( :base => self.base_dn,
 145                       :filter => search_filter,
 146                       :attributes=> search_attributes) do |entry|
 147 
 148        if onthefly_register?
 149          attrs = get_user_attributes_from_ldap_entry(entry)
 150        else
 151          attrs = {:dn => entry.dn}
 152        end
 153 
 154        logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
 155      end
 156 
 157      attrs
 158    end
 159 
 160    def self.get_attr(entry, attr_name)
 161      if !attr_name.blank?
 162        entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
 163      end
 164    end
 165  end