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
|