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
|