Project

General

Profile

Patch #3358 » advanced_ldap_auth_r2743.diff

LDAP authentication patch for revision 2743 - Daniel Marczisovszky, 2009-05-13 21:24

View differences:

app/models/auth_source_ldap.rb (working copy)
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 
22 25
  validates_presence_of :host, :port, :attr_login
23 26
  validates_length_of :name, :host, :account_password, :maximum => 60, :allow_nil => true
24
  validates_length_of :account, :base_dn, :maximum => 255, :allow_nil => true
27
  validates_length_of :account, :base_dn, :filter, :maximum => 255, :allow_nil => true
25 28
  validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
26
  validates_numericality_of :port, :only_integer => true
27
  
29
  validates_numericality_of :port, :protocol_version, :only_integer => true
30

  
28 31
  before_validation :strip_ldap_attributes
29
  
32

  
30 33
  def after_initialize
31 34
    self.port = 389 if self.port == 0
35
    self.protocol_version = 3 if self.protocol_version == 0
32 36
  end
33
  
37

  
34 38
  def authenticate(login, password)
35 39
    return nil if login.blank? || password.blank?
36 40
    attrs = []
37 41
    # 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", "*" ) 
42

  
43
    # Ticket #1913 by Adi Kriegisch (adi@cg.tuwien.ac.at)
44
    if self.account.include? "$login" then
45
      logger.debug "LDAP-Auth with User login" if logger && logger.debug?
46
      ldap_con = initialize_ldap_con(self.account.sub("$login", encode(login)), password)
47
    else
48
      logger.debug "LDAP-Auth with Admin User" if logger && logger.debug?
49
      ldap_con = initialize_ldap_con(self.account, self.account_password)
50
    end
51

  
52
    if self.filter.empty?
53
      filter = self.attr_login + "=" + encode(login)
54
    else
55
      filter = self.filter.gsub("$login", encode(login))
56
    end
41 57
    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|
58
    logger.debug "Search in DN: #{self.base_dn} with filter: #{filter}" if logger && logger.debug?
59
    ldap_con.search( self.base_dn, LDAP::LDAP_SCOPE_SUBTREE, filter,
60
                     (onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) { |entry|
46 61
      dn = entry.dn
47 62
      attrs = [:firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
48 63
               :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
49 64
               :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
50 65
               :auth_source_id => self.id ] if onthefly_register?
51
    end
66
    }
52 67
    return nil if dn.empty?
53 68
    logger.debug "DN found for #{login}: #{dn}" if logger && logger.debug?
54 69
    # authenticate user
55
    ldap_con = initialize_ldap_con(dn, password)
56
    return nil unless ldap_con.bind
70
    begin
71
      logger.debug "Trying to login as #{dn}" if logger && logger.debug?
72
      initialize_ldap_con(dn, password)
73
    rescue LDAP::Error => bindError
74
      logger.debug "Login failed: #{bindError}" if logger && logger.debug?
75
      return nil
76
    end
57 77
    # return user's attributes
58 78
    logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
59
    attrs    
60
  rescue  Net::LDAP::LdapError => text
61
    raise "LdapError: " + text
79
    attrs
80
  rescue  LDAP::Error => text
81
    raise "LDAP Auth Error: " + text
62 82
  end
63 83

  
64 84
  # test the connection to the LDAP
65 85
  def test_connection
66 86
    ldap_con = initialize_ldap_con(self.account, self.account_password)
67
    ldap_con.open { }
68
  rescue  Net::LDAP::LdapError => text
69
    raise "LdapError: " + text
87
  rescue  LDAP::Error => text
88
    raise "LDAP Error: " + text
70 89
  end
71
 
90

  
72 91
  def auth_method_name
73 92
    "LDAP"
74 93
  end
75
  
94

  
76 95
  private
77
  
96

  
78 97
  def strip_ldap_attributes
79 98
    [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
80 99
      write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
81 100
    end
82 101
  end
83
  
102

  
84 103
  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
104
    logger.debug "Connecting to #{self.host}:#{self.port}, tls=#{self.tls}" if logger && logger.debug?
105
    if self.tls
106
      conn = LDAP::SSLConn.new(self.host, self.port, self.starttls)
107
    else
108
      conn = LDAP::Conn.new(self.host, self.port)
109
    end
110
    logger.debug "Dereference set option" if logger && logger.debug?
111
    conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, self.protocol_version)
112
    conn.set_option(LDAP::LDAP_OPT_DEREF, self.dereference)
113
    if self.tls && self.starttls
114
      logger.debug "Certificate set option" if logger && logger.debug?
115
      conn.set_option(LDAP::LDAP_OPT_X_TLS_REQUIRE_CERT, self.require_cert)
116
    end
117

  
118
    logger.debug "Trying to bind" if logger && logger.debug?
119
    if !ldap_user.blank? || !ldap_password.blank? then
120
      logger.debug "Bind as user #{ldap_user}" if logger && logger.debug?
121
      conn.bind(ldap_user, ldap_password)
122
    else
123
      logger.debug "Anonymous bind" if logger && logger.debug?
124
      conn.bind
125
    end
126
  rescue LDAP::Error => text
127
    logger.debug "LDAP Connect Error: #{$!}" if logger && logger.debug?
128
    raise
91 129
  end
92
  
130

  
93 131
  def self.get_attr(entry, attr_name)
94 132
    if !attr_name.blank?
95 133
      entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
96 134
    end
97 135
  end
136

  
137
  def encode(value)
138
    value = value.gsub("\\", "\\\\5c")
139
    value = value.gsub("*", "\\\\2a")
140
    value = value.gsub("(", "\\\\28")
141
    value = value.gsub(")", "\\\\29")
142
    value = value.gsub("\000", "\\\\00")
143
  end
98 144
end
app/views/auth_sources/_form.rhtml (working copy)
9 9
<%= text_field 'auth_source', 'host'  %></p>
10 10

  
11 11
<p><label for="auth_source_port"><%=l(:field_port)%> <span class="required">*</span></label>
12
<%= text_field 'auth_source', 'port', :size => 6 %> <%= check_box 'auth_source', 'tls'  %> LDAPS</p>
12
<%= text_field 'auth_source', 'port', :size => 6 %>
13
<%= check_box 'auth_source', 'tls' %> LDAPS
14
<%= check_box 'auth_source', 'starttls' %> START_TLS</p>
13 15

  
16
<p><label for="auth_source_protocol_version"><%=l(:field_protocol_version)%></label>
17
<%= select 'auth_source', 'protocol_version', [2, 3] %></p>
18

  
14 19
<p><label for="auth_source_account"><%=l(:field_account)%></label>
15
<%= text_field 'auth_source', 'account'  %></p>
20
<%= text_field 'auth_source', 'account' %></p>
16 21

  
17 22
<p><label for="auth_source_account_password"><%=l(:field_password)%></label>
18 23
<%= password_field 'auth_source', 'account_password', :name => 'ignore',
......
20 25
                                           :onfocus => "this.value=''; this.name='auth_source[account_password]';",
21 26
                                           :onchange => "this.name='auth_source[account_password]';" %></p>
22 27

  
28
<p><label for="auth_source_require_cert"><%=l(:field_require_cert)%></label>
29
<%= select 'auth_source', 'require_cert', [ ["Never", 0], ["Hard", 1], ["Demand", 2], ["Allow", 3], ["Try", 4] ] %></p>
30

  
23 31
<p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label>
24 32
<%= text_field 'auth_source', 'base_dn', :size => 60 %></p>
25 33

  
34
<p><label for="auth_source_filter"><%=l(:field_filter)%></label><%= text_field 'auth_source', 'filter' %></p>
35

  
36
<p><label for="auth_source_dereference"><%=l(:field_dereference)%></label>
37
<%= select 'auth_source', 'dereference', [ ["Never", 0], ["Searching", 1], ["Finding", 2], ["Always", 3] ] %></p>
38

  
26 39
<p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label>
27 40
<%= check_box 'auth_source', 'onthefly_register' %></p>
28 41
</div>
config/locales/en.yml (working copy)
209 209
  field_port: Port
210 210
  field_account: Account
211 211
  field_base_dn: Base DN
212
  field_protocol_version: Protocol version
213
  field_filter: Filter
214
  field_dereference: Dereference
215
  field_require_cert: Require certificate
212 216
  field_attr_login: Login attribute
213 217
  field_attr_firstname: Firstname attribute
214 218
  field_attr_lastname: Lastname attribute
db/migrate/20090513210330_add_auth_sources_filter_deref_advtls.rb (revision 0)
1
class AddAuthSourcesFilterDerefAdvtls < ActiveRecord::Migration
2
  def self.up
3
    add_column :auth_sources, :starttls, :boolean, :default => false, :null => false
4
    add_column :auth_sources, :filter, :string
5
    add_column :auth_sources, :dereference, :integer
6
    add_column :auth_sources, :require_cert, :integer
7
    add_column :auth_sources, :protocol_version, :integer, :default => 3, :null => false
8
  end
9

  
10
  def self.down
11
    remove_column :auth_sources, :starttls
12
    remove_column :auth_sources, :filter
13
    remove_column :auth_sources, :dereference
14
    remove_column :auth_sources, :require_cert
15
    remove_column :auth_sources, :protocol_version
16
  end
17
end
(2-2/9)