| 15 | 
  15 | 
  
    # along with this program; if not, write to the Free Software 
   | 
  | 16 | 
  16 | 
  
    # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
   | 
  | 17 | 
  17 | 
  
    
   | 
  | 18 | 
   | 
  
    require 'net/ldap' 
   | 
   | 
  18 | 
  
    # Modified by Daniel Marczisovszky (marczi@dev-labs.com)  
   | 
   | 
  19 | 
  
    # to allow dereferencing aliases, START_TLS 
   | 
   | 
  20 | 
  
    
   | 
   | 
  21 | 
  
    require 'ldap' 
   | 
  | 19 | 
  22 | 
  
    require 'iconv' 
   | 
  | 20 | 
  23 | 
  
    
   | 
  | 21 | 
  24 | 
  
    class AuthSourceLdap < AuthSource  
   | 
  | ... | ... |  | 
  | 24 | 
  27 | 
  
      validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true 
   | 
  | 25 | 
  28 | 
  
      validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true 
   | 
  | 26 | 
  29 | 
  
      validates_numericality_of :port, :only_integer => true 
   | 
  | 27 | 
   | 
  
       
   | 
   | 
  30 | 
  
    
   | 
  | 28 | 
  31 | 
  
      before_validation :strip_ldap_attributes 
   | 
  | 29 | 
   | 
  
       
   | 
   | 
  32 | 
  
    
   | 
  | 30 | 
  33 | 
  
      def after_initialize 
   | 
  | 31 | 
  34 | 
  
        self.port = 389 if self.port == 0 
   | 
  | 32 | 
  35 | 
  
      end 
   | 
  | 33 | 
   | 
  
       
   | 
   | 
  36 | 
  
    
   | 
  | 34 | 
  37 | 
  
      def authenticate(login, password) 
   | 
  | 35 | 
  38 | 
  
        return nil if login.blank? || password.blank? 
   | 
  | 36 | 
  39 | 
  
        attrs = [] 
   | 
  | 37 | 
  40 | 
  
        # get user's DN 
   | 
  | 38 | 
   | 
  
        ldap_con = initialize_ldap_con(self.account, self.account_password) 
   | 
  | 39 | 
   | 
  
        login_filter = Net::LDAP::Filter.eq( self.attr_login, login )  
   | 
  | 40 | 
   | 
  
        object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )  
   | 
   | 
  41 | 
  
    
   | 
   | 
  42 | 
  
        # Ticket #1913 by Adi Kriegisch (adi@cg.tuwien.ac.at) 
   | 
   | 
  43 | 
  
        if self.account.include? "$login" then 
   | 
   | 
  44 | 
  
          logger.debug "LDAP-Auth with User login" if logger && logger.debug? 
   | 
   | 
  45 | 
  
          ldap_con = initialize_ldap_con(self.account.sub("$login", encode(login)), password)
   | 
   | 
  46 | 
  
        else 
   | 
   | 
  47 | 
  
          logger.debug "LDAP-Auth with Admin User" if logger && logger.debug? 
   | 
   | 
  48 | 
  
          ldap_con = initialize_ldap_con(self.account, self.account_password) 
   | 
   | 
  49 | 
  
        end 
   | 
   | 
  50 | 
  
    
   | 
   | 
  51 | 
  
        if self.filter.empty? 
   | 
   | 
  52 | 
  
          filter = self.attr_login + "=" + encode(login) 
   | 
   | 
  53 | 
  
        else 
   | 
   | 
  54 | 
  
          filter = self.filter.gsub("$login", encode(login))
   | 
   | 
  55 | 
  
        end 
   | 
  | 41 | 
  56 | 
  
        dn = String.new 
   | 
  | 42 | 
   | 
  
        ldap_con.search( :base => self.base_dn,  
   | 
  | 43 | 
   | 
  
                         :filter => object_filter & login_filter,  
   | 
  | 44 | 
   | 
  
                         # only ask for the DN if on-the-fly registration is disabled 
   | 
  | 45 | 
   | 
  
                         :attributes=> (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) do |entry| 
   | 
   | 
  57 | 
  
        logger.debug "Search in DN: #{self.base_dn} with filter: #{filter}" if logger && logger.debug?
   | 
   | 
  58 | 
  
        ldap_con.search( self.base_dn, LDAP::LDAP_SCOPE_SUBTREE, filter, 
   | 
   | 
  59 | 
  
                         (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) { |entry|
   | 
  | 46 | 
  60 | 
  
          dn = entry.dn 
   | 
  | 47 | 
  61 | 
  
          attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname), 
   | 
  | 48 | 
  62 | 
  
                   :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname), 
   | 
  | 49 | 
  63 | 
  
                   :mail => AuthSourceLdap.get_attr(entry, self.attr_mail), 
   | 
  | 50 | 
  64 | 
  
                   :auth_source_id => self.id ] if onthefly_register? 
   | 
  | 51 | 
   | 
  
        end 
   | 
   | 
  65 | 
  
        } 
   | 
  | 52 | 
  66 | 
  
        return nil if dn.empty? 
   | 
  | 53 | 
  67 | 
  
        logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
   | 
  | 54 | 
  68 | 
  
        # authenticate user 
   | 
  | 55 | 
   | 
  
        ldap_con = initialize_ldap_con(dn, password) 
   | 
  | 56 | 
   | 
  
        return nil unless ldap_con.bind 
   | 
   | 
  69 | 
  
        ldap_con.unbind 
   | 
   | 
  70 | 
  
        begin 
   | 
   | 
  71 | 
  
          result = ldap_con.bind(dn, password) 
   | 
   | 
  72 | 
  
        rescue LDAP::Error => bindError 
   | 
   | 
  73 | 
  
          return nil 
   | 
   | 
  74 | 
  
        end 
   | 
  | 57 | 
  75 | 
  
        # return user's attributes 
   | 
  | 58 | 
  76 | 
  
        logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
   | 
  | 59 | 
   | 
  
        attrs     
   | 
  | 60 | 
   | 
  
      rescue  Net::LDAP::LdapError => text 
   | 
   | 
  77 | 
  
        attrs 
   | 
   | 
  78 | 
  
      rescue  LDAP::Error => text 
   | 
  | 61 | 
  79 | 
  
        raise "LdapError: " + text 
   | 
  | 62 | 
  80 | 
  
      end 
   | 
  | 63 | 
  81 | 
  
    
   | 
  | 64 | 
  82 | 
  
      # test the connection to the LDAP 
   | 
  | 65 | 
  83 | 
  
      def test_connection 
   | 
  | 66 | 
  84 | 
  
        ldap_con = initialize_ldap_con(self.account, self.account_password) 
   | 
  | 67 | 
   | 
  
        ldap_con.open { }
   | 
  | 68 | 
   | 
  
      rescue  Net::LDAP::LdapError => text 
   | 
   | 
  85 | 
  
      rescue  LDAP::Error => text 
   | 
  | 69 | 
  86 | 
  
        raise "LdapError: " + text 
   | 
  | 70 | 
  87 | 
  
      end 
   | 
  | 71 | 
   | 
  
      
   | 
   | 
  88 | 
  
    
   | 
  | 72 | 
  89 | 
  
      def auth_method_name 
   | 
  | 73 | 
  90 | 
  
        "LDAP" 
   | 
  | 74 | 
  91 | 
  
      end 
   | 
  | 75 | 
   | 
  
       
   | 
   | 
  92 | 
  
    
   | 
  | 76 | 
  93 | 
  
      private 
   | 
  | 77 | 
   | 
  
       
   | 
   | 
  94 | 
  
    
   | 
  | 78 | 
  95 | 
  
      def strip_ldap_attributes 
   | 
  | 79 | 
  96 | 
  
        [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr| 
   | 
  | 80 | 
  97 | 
  
          write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil? 
   | 
  | 81 | 
  98 | 
  
        end 
   | 
  | 82 | 
  99 | 
  
      end 
   | 
  | 83 | 
   | 
  
       
   | 
   | 
  100 | 
  
    
   | 
  | 84 | 
  101 | 
  
      def initialize_ldap_con(ldap_user, ldap_password) 
   | 
  | 85 | 
   | 
  
        options = { :host => self.host,
   | 
  | 86 | 
   | 
  
                    :port => self.port, 
   | 
  | 87 | 
   | 
  
                    :encryption => (self.tls ? :simple_tls : nil) 
   | 
  | 88 | 
   | 
  
                  } 
   | 
  | 89 | 
   | 
  
        options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
   | 
  | 90 | 
   | 
  
        Net::LDAP.new options 
   | 
   | 
  102 | 
  
        if self.tls 
   | 
   | 
  103 | 
  
          conn = LDAP::SSLConn.new(self.host, self.port, self.starttls) 
   | 
   | 
  104 | 
  
        else 
   | 
   | 
  105 | 
  
          conn = LDAP::Conn.new(self.host, self.port) 
   | 
   | 
  106 | 
  
        end 
   | 
   | 
  107 | 
  
        conn.set_option(LDAP::LDAP_OPT_DEREF, self.dereference) 
   | 
   | 
  108 | 
  
        conn.set_option(LDAP::LDAP_OPT_X_TLS_REQUIRE_CERT, self.require_cert) 
   | 
   | 
  109 | 
  
    
   | 
   | 
  110 | 
  
        if !ldap_user.blank? || !ldap_password.blank? then 
   | 
   | 
  111 | 
  
          logger.debug "Bind as user #{ldap_user}" if logger && logger.debug?
   | 
   | 
  112 | 
  
          conn.bind(ldap_user, ldap_password) 
   | 
   | 
  113 | 
  
        else 
   | 
   | 
  114 | 
  
          logger.debug "Anonymous bind" if logger && logger.debug? 
   | 
   | 
  115 | 
  
          conn.bind 
   | 
   | 
  116 | 
  
        end 
   | 
  | 91 | 
  117 | 
  
      end 
   | 
  | 92 | 
   | 
  
       
   | 
   | 
  118 | 
  
    
   | 
  | 93 | 
  119 | 
  
      def self.get_attr(entry, attr_name) 
   | 
  | 94 | 
  120 | 
  
        if !attr_name.blank? 
   | 
  | 95 | 
  121 | 
  
          entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name] 
   | 
  | 96 | 
  122 | 
  
        end 
   | 
  | 97 | 
  123 | 
  
      end 
   | 
   | 
  124 | 
  
    
   | 
   | 
  125 | 
  
      def encode(value) 
   | 
   | 
  126 | 
  
        value = value.gsub("\\", "\\\\5c")
   | 
   | 
  127 | 
  
        value = value.gsub("*", "\\\\2a")
   | 
   | 
  128 | 
  
        value = value.gsub("(", "\\\\28")
   | 
   | 
  129 | 
  
        value = value.gsub(")", "\\\\29")
   | 
   | 
  130 | 
  
        value = value.gsub("\000", "\\\\00")
   | 
   | 
  131 | 
  
      end 
   | 
  | 98 | 
  132 | 
  
    end 
   |