Mercurial > hg > soundsoftware-site
comparison .svn/pristine/df/df9bbf954b4f906004aaa967b48274754ad6dcd0.svn-base @ 1298:4f746d8966dd redmine_2.3_integration
Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author | Chris Cannam |
---|---|
date | Fri, 14 Jun 2013 09:28:30 +0100 |
parents | 622f24f53b42 |
children |
comparison
equal
deleted
inserted
replaced
1297:0a574315af3e | 1298:4f746d8966dd |
---|---|
1 # Redmine - project management software | |
2 # Copyright (C) 2006-2013 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 'net/ldap' | |
19 require 'net/ldap/dn' | |
20 require 'timeout' | |
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 validates_numericality_of :timeout, :only_integer => true, :allow_blank => true | |
29 validate :validate_filter | |
30 | |
31 before_validation :strip_ldap_attributes | |
32 | |
33 def initialize(attributes=nil, *args) | |
34 super | |
35 self.port = 389 if self.port == 0 | |
36 end | |
37 | |
38 def authenticate(login, password) | |
39 return nil if login.blank? || password.blank? | |
40 | |
41 with_timeout do | |
42 attrs = get_user_dn(login, password) | |
43 if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password) | |
44 logger.debug "Authentication successful for '#{login}'" if logger && logger.debug? | |
45 return attrs.except(:dn) | |
46 end | |
47 end | |
48 rescue Net::LDAP::LdapError => e | |
49 raise AuthSourceException.new(e.message) | |
50 end | |
51 | |
52 # test the connection to the LDAP | |
53 def test_connection | |
54 with_timeout do | |
55 ldap_con = initialize_ldap_con(self.account, self.account_password) | |
56 ldap_con.open { } | |
57 end | |
58 rescue Net::LDAP::LdapError => e | |
59 raise AuthSourceException.new(e.message) | |
60 end | |
61 | |
62 def auth_method_name | |
63 "LDAP" | |
64 end | |
65 | |
66 # Returns true if this source can be searched for users | |
67 def searchable? | |
68 !account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send("attr_#{a}?")} | |
69 end | |
70 | |
71 # Searches the source for users and returns an array of results | |
72 def search(q) | |
73 q = q.to_s.strip | |
74 return [] unless searchable? && q.present? | |
75 | |
76 results = [] | |
77 search_filter = base_filter & Net::LDAP::Filter.begins(self.attr_login, q) | |
78 ldap_con = initialize_ldap_con(self.account, self.account_password) | |
79 ldap_con.search(:base => self.base_dn, | |
80 :filter => search_filter, | |
81 :attributes => ['dn', self.attr_login, self.attr_firstname, self.attr_lastname, self.attr_mail], | |
82 :size => 10) do |entry| | |
83 attrs = get_user_attributes_from_ldap_entry(entry) | |
84 attrs[:login] = AuthSourceLdap.get_attr(entry, self.attr_login) | |
85 results << attrs | |
86 end | |
87 results | |
88 rescue Net::LDAP::LdapError => e | |
89 raise AuthSourceException.new(e.message) | |
90 end | |
91 | |
92 private | |
93 | |
94 def with_timeout(&block) | |
95 timeout = self.timeout | |
96 timeout = 20 unless timeout && timeout > 0 | |
97 Timeout.timeout(timeout) do | |
98 return yield | |
99 end | |
100 rescue Timeout::Error => e | |
101 raise AuthSourceTimeoutException.new(e.message) | |
102 end | |
103 | |
104 def ldap_filter | |
105 if filter.present? | |
106 Net::LDAP::Filter.construct(filter) | |
107 end | |
108 rescue Net::LDAP::LdapError | |
109 nil | |
110 end | |
111 | |
112 def base_filter | |
113 filter = Net::LDAP::Filter.eq("objectClass", "*") | |
114 if f = ldap_filter | |
115 filter = filter & f | |
116 end | |
117 filter | |
118 end | |
119 | |
120 def validate_filter | |
121 if filter.present? && ldap_filter.nil? | |
122 errors.add(:filter, :invalid) | |
123 end | |
124 end | |
125 | |
126 def strip_ldap_attributes | |
127 [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr| | |
128 write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil? | |
129 end | |
130 end | |
131 | |
132 def initialize_ldap_con(ldap_user, ldap_password) | |
133 options = { :host => self.host, | |
134 :port => self.port, | |
135 :encryption => (self.tls ? :simple_tls : nil) | |
136 } | |
137 options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank? | |
138 Net::LDAP.new options | |
139 end | |
140 | |
141 def get_user_attributes_from_ldap_entry(entry) | |
142 { | |
143 :dn => entry.dn, | |
144 :firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname), | |
145 :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname), | |
146 :mail => AuthSourceLdap.get_attr(entry, self.attr_mail), | |
147 :auth_source_id => self.id | |
148 } | |
149 end | |
150 | |
151 # Return the attributes needed for the LDAP search. It will only | |
152 # include the user attributes if on-the-fly registration is enabled | |
153 def search_attributes | |
154 if onthefly_register? | |
155 ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] | |
156 else | |
157 ['dn'] | |
158 end | |
159 end | |
160 | |
161 # Check if a DN (user record) authenticates with the password | |
162 def authenticate_dn(dn, password) | |
163 if dn.present? && password.present? | |
164 initialize_ldap_con(dn, password).bind | |
165 end | |
166 end | |
167 | |
168 # Get the user's dn and any attributes for them, given their login | |
169 def get_user_dn(login, password) | |
170 ldap_con = nil | |
171 if self.account && self.account.include?("$login") | |
172 ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password) | |
173 else | |
174 ldap_con = initialize_ldap_con(self.account, self.account_password) | |
175 end | |
176 attrs = {} | |
177 search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login) | |
178 | |
179 ldap_con.search( :base => self.base_dn, | |
180 :filter => search_filter, | |
181 :attributes=> search_attributes) do |entry| | |
182 | |
183 if onthefly_register? | |
184 attrs = get_user_attributes_from_ldap_entry(entry) | |
185 else | |
186 attrs = {:dn => entry.dn} | |
187 end | |
188 | |
189 logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug? | |
190 end | |
191 | |
192 attrs | |
193 end | |
194 | |
195 def self.get_attr(entry, attr_name) | |
196 if !attr_name.blank? | |
197 entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name] | |
198 end | |
199 end | |
200 end |