##// END OF EJS Templates
LDAP: adds the ability to bind with user's account (#1913)....
Jean-Philippe Lang -
r9121:fdeb398c5e06
parent child
Show More
@@ -1,151 +1,157
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 require 'iconv'
19 19 require 'net/ldap'
20 require 'net/ldap/dn'
20 21
21 22 class AuthSourceLdap < AuthSource
22 23 validates_presence_of :host, :port, :attr_login
23 24 validates_length_of :name, :host, :maximum => 60, :allow_nil => true
24 25 validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
25 26 validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
26 27 validates_numericality_of :port, :only_integer => true
27 28 validate :validate_filter
28 29
29 30 before_validation :strip_ldap_attributes
30 31
31 32 def initialize(attributes=nil, *args)
32 33 super
33 34 self.port = 389 if self.port == 0
34 35 end
35 36
36 37 def authenticate(login, password)
37 38 return nil if login.blank? || password.blank?
38 attrs = get_user_dn(login)
39 attrs = get_user_dn(login, password)
39 40
40 41 if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
41 42 logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
42 43 return attrs.except(:dn)
43 44 end
44 45 rescue Net::LDAP::LdapError => e
45 46 raise AuthSourceException.new(e.message)
46 47 end
47 48
48 49 # test the connection to the LDAP
49 50 def test_connection
50 51 ldap_con = initialize_ldap_con(self.account, self.account_password)
51 52 ldap_con.open { }
52 53 rescue Net::LDAP::LdapError => e
53 54 raise "LdapError: " + e.message
54 55 end
55 56
56 57 def auth_method_name
57 58 "LDAP"
58 59 end
59 60
60 61 private
61 62
62 63 def ldap_filter
63 64 if filter.present?
64 65 Net::LDAP::Filter.construct(filter)
65 66 end
66 67 rescue Net::LDAP::LdapError
67 68 nil
68 69 end
69 70
70 71 def validate_filter
71 72 if filter.present? && ldap_filter.nil?
72 73 errors.add(:filter, :invalid)
73 74 end
74 75 end
75 76
76 77 def strip_ldap_attributes
77 78 [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
78 79 write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
79 80 end
80 81 end
81 82
82 83 def initialize_ldap_con(ldap_user, ldap_password)
83 84 options = { :host => self.host,
84 85 :port => self.port,
85 86 :encryption => (self.tls ? :simple_tls : nil)
86 87 }
87 88 options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
88 89 Net::LDAP.new options
89 90 end
90 91
91 92 def get_user_attributes_from_ldap_entry(entry)
92 93 {
93 94 :dn => entry.dn,
94 95 :firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
95 96 :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
96 97 :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
97 98 :auth_source_id => self.id
98 99 }
99 100 end
100 101
101 102 # Return the attributes needed for the LDAP search. It will only
102 103 # include the user attributes if on-the-fly registration is enabled
103 104 def search_attributes
104 105 if onthefly_register?
105 106 ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]
106 107 else
107 108 ['dn']
108 109 end
109 110 end
110 111
111 112 # Check if a DN (user record) authenticates with the password
112 113 def authenticate_dn(dn, password)
113 114 if dn.present? && password.present?
114 115 initialize_ldap_con(dn, password).bind
115 116 end
116 117 end
117 118
118 119 # Get the user's dn and any attributes for them, given their login
119 def get_user_dn(login)
120 ldap_con = initialize_ldap_con(self.account, self.account_password)
120 def get_user_dn(login, password)
121 ldap_con = nil
122 if self.account && self.account.include?("login")
123 ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password)
124 else
125 ldap_con = initialize_ldap_con(self.account, self.account_password)
126 end
121 127 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
122 128 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
123 129 attrs = {}
124 130
125 131 search_filter = object_filter & login_filter
126 132 if f = ldap_filter
127 133 search_filter = search_filter & f
128 134 end
129 135
130 136 ldap_con.search( :base => self.base_dn,
131 137 :filter => search_filter,
132 138 :attributes=> search_attributes) do |entry|
133 139
134 140 if onthefly_register?
135 141 attrs = get_user_attributes_from_ldap_entry(entry)
136 142 else
137 143 attrs = {:dn => entry.dn}
138 144 end
139 145
140 146 logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
141 147 end
142 148
143 149 attrs
144 150 end
145 151
146 152 def self.get_attr(entry, attr_name)
147 153 if !attr_name.blank?
148 154 entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
149 155 end
150 156 end
151 157 end
@@ -1,884 +1,933
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
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 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class UserTest < ActiveSupport::TestCase
21 21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
22 22 :trackers, :issue_statuses,
23 23 :projects_trackers,
24 24 :watchers,
25 25 :issue_categories, :enumerations, :issues,
26 26 :journals, :journal_details,
27 27 :groups_users,
28 28 :enabled_modules,
29 29 :workflows
30 30
31 31 def setup
32 32 @admin = User.find(1)
33 33 @jsmith = User.find(2)
34 34 @dlopper = User.find(3)
35 35 end
36 36
37 37 test 'object_daddy creation' do
38 38 User.generate_with_protected!(:firstname => 'Testing connection')
39 39 User.generate_with_protected!(:firstname => 'Testing connection')
40 40 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
41 41 end
42 42
43 43 def test_truth
44 44 assert_kind_of User, @jsmith
45 45 end
46 46
47 47 def test_mail_should_be_stripped
48 48 u = User.new
49 49 u.mail = " foo@bar.com "
50 50 assert_equal "foo@bar.com", u.mail
51 51 end
52 52
53 53 def test_mail_validation
54 54 u = User.new
55 55 u.mail = ''
56 56 assert !u.valid?
57 57 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
58 58 end
59 59
60 60 def test_login_length_validation
61 61 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
62 62 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
63 63 assert !user.valid?
64 64
65 65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
66 66 assert user.valid?
67 67 assert user.save
68 68 end
69 69
70 70 def test_create
71 71 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
72 72
73 73 user.login = "jsmith"
74 74 user.password, user.password_confirmation = "password", "password"
75 75 # login uniqueness
76 76 assert !user.save
77 77 assert_equal 1, user.errors.count
78 78
79 79 user.login = "newuser"
80 80 user.password, user.password_confirmation = "passwd", "password"
81 81 # password confirmation
82 82 assert !user.save
83 83 assert_equal 1, user.errors.count
84 84
85 85 user.password, user.password_confirmation = "password", "password"
86 86 assert user.save
87 87 end
88 88
89 89 context "User#before_create" do
90 90 should "set the mail_notification to the default Setting" do
91 91 @user1 = User.generate_with_protected!
92 92 assert_equal 'only_my_events', @user1.mail_notification
93 93
94 94 with_settings :default_notification_option => 'all' do
95 95 @user2 = User.generate_with_protected!
96 96 assert_equal 'all', @user2.mail_notification
97 97 end
98 98 end
99 99 end
100 100
101 101 context "User.login" do
102 102 should "be case-insensitive." do
103 103 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
104 104 u.login = 'newuser'
105 105 u.password, u.password_confirmation = "password", "password"
106 106 assert u.save
107 107
108 108 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
109 109 u.login = 'NewUser'
110 110 u.password, u.password_confirmation = "password", "password"
111 111 assert !u.save
112 112 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
113 113 end
114 114 end
115 115
116 116 def test_mail_uniqueness_should_not_be_case_sensitive
117 117 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
118 118 u.login = 'newuser1'
119 119 u.password, u.password_confirmation = "password", "password"
120 120 assert u.save
121 121
122 122 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
123 123 u.login = 'newuser2'
124 124 u.password, u.password_confirmation = "password", "password"
125 125 assert !u.save
126 126 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
127 127 end
128 128
129 129 def test_update
130 130 assert_equal "admin", @admin.login
131 131 @admin.login = "john"
132 132 assert @admin.save, @admin.errors.full_messages.join("; ")
133 133 @admin.reload
134 134 assert_equal "john", @admin.login
135 135 end
136 136
137 137 def test_destroy_should_delete_members_and_roles
138 138 members = Member.find_all_by_user_id(2)
139 139 ms = members.size
140 140 rs = members.collect(&:roles).flatten.size
141 141
142 142 assert_difference 'Member.count', - ms do
143 143 assert_difference 'MemberRole.count', - rs do
144 144 User.find(2).destroy
145 145 end
146 146 end
147 147
148 148 assert_nil User.find_by_id(2)
149 149 assert Member.find_all_by_user_id(2).empty?
150 150 end
151 151
152 152 def test_destroy_should_update_attachments
153 153 attachment = Attachment.create!(:container => Project.find(1),
154 154 :file => uploaded_test_file("testfile.txt", "text/plain"),
155 155 :author_id => 2)
156 156
157 157 User.find(2).destroy
158 158 assert_nil User.find_by_id(2)
159 159 assert_equal User.anonymous, attachment.reload.author
160 160 end
161 161
162 162 def test_destroy_should_update_comments
163 163 comment = Comment.create!(
164 164 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
165 165 :author => User.find(2),
166 166 :comments => 'foo'
167 167 )
168 168
169 169 User.find(2).destroy
170 170 assert_nil User.find_by_id(2)
171 171 assert_equal User.anonymous, comment.reload.author
172 172 end
173 173
174 174 def test_destroy_should_update_issues
175 175 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
176 176
177 177 User.find(2).destroy
178 178 assert_nil User.find_by_id(2)
179 179 assert_equal User.anonymous, issue.reload.author
180 180 end
181 181
182 182 def test_destroy_should_unassign_issues
183 183 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
184 184
185 185 User.find(2).destroy
186 186 assert_nil User.find_by_id(2)
187 187 assert_nil issue.reload.assigned_to
188 188 end
189 189
190 190 def test_destroy_should_update_journals
191 191 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
192 192 issue.init_journal(User.find(2), "update")
193 193 issue.save!
194 194
195 195 User.find(2).destroy
196 196 assert_nil User.find_by_id(2)
197 197 assert_equal User.anonymous, issue.journals.first.reload.user
198 198 end
199 199
200 200 def test_destroy_should_update_journal_details_old_value
201 201 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
202 202 issue.init_journal(User.find(1), "update")
203 203 issue.assigned_to_id = nil
204 204 assert_difference 'JournalDetail.count' do
205 205 issue.save!
206 206 end
207 207 journal_detail = JournalDetail.first(:order => 'id DESC')
208 208 assert_equal '2', journal_detail.old_value
209 209
210 210 User.find(2).destroy
211 211 assert_nil User.find_by_id(2)
212 212 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
213 213 end
214 214
215 215 def test_destroy_should_update_journal_details_value
216 216 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
217 217 issue.init_journal(User.find(1), "update")
218 218 issue.assigned_to_id = 2
219 219 assert_difference 'JournalDetail.count' do
220 220 issue.save!
221 221 end
222 222 journal_detail = JournalDetail.first(:order => 'id DESC')
223 223 assert_equal '2', journal_detail.value
224 224
225 225 User.find(2).destroy
226 226 assert_nil User.find_by_id(2)
227 227 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
228 228 end
229 229
230 230 def test_destroy_should_update_messages
231 231 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
232 232 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
233 233
234 234 User.find(2).destroy
235 235 assert_nil User.find_by_id(2)
236 236 assert_equal User.anonymous, message.reload.author
237 237 end
238 238
239 239 def test_destroy_should_update_news
240 240 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
241 241
242 242 User.find(2).destroy
243 243 assert_nil User.find_by_id(2)
244 244 assert_equal User.anonymous, news.reload.author
245 245 end
246 246
247 247 def test_destroy_should_delete_private_queries
248 248 query = Query.new(:name => 'foo', :is_public => false)
249 249 query.project_id = 1
250 250 query.user_id = 2
251 251 query.save!
252 252
253 253 User.find(2).destroy
254 254 assert_nil User.find_by_id(2)
255 255 assert_nil Query.find_by_id(query.id)
256 256 end
257 257
258 258 def test_destroy_should_update_public_queries
259 259 query = Query.new(:name => 'foo', :is_public => true)
260 260 query.project_id = 1
261 261 query.user_id = 2
262 262 query.save!
263 263
264 264 User.find(2).destroy
265 265 assert_nil User.find_by_id(2)
266 266 assert_equal User.anonymous, query.reload.user
267 267 end
268 268
269 269 def test_destroy_should_update_time_entries
270 270 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
271 271 entry.project_id = 1
272 272 entry.user_id = 2
273 273 entry.save!
274 274
275 275 User.find(2).destroy
276 276 assert_nil User.find_by_id(2)
277 277 assert_equal User.anonymous, entry.reload.user
278 278 end
279 279
280 280 def test_destroy_should_delete_tokens
281 281 token = Token.create!(:user_id => 2, :value => 'foo')
282 282
283 283 User.find(2).destroy
284 284 assert_nil User.find_by_id(2)
285 285 assert_nil Token.find_by_id(token.id)
286 286 end
287 287
288 288 def test_destroy_should_delete_watchers
289 289 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
290 290 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
291 291
292 292 User.find(2).destroy
293 293 assert_nil User.find_by_id(2)
294 294 assert_nil Watcher.find_by_id(watcher.id)
295 295 end
296 296
297 297 def test_destroy_should_update_wiki_contents
298 298 wiki_content = WikiContent.create!(
299 299 :text => 'foo',
300 300 :author_id => 2,
301 301 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
302 302 )
303 303 wiki_content.text = 'bar'
304 304 assert_difference 'WikiContent::Version.count' do
305 305 wiki_content.save!
306 306 end
307 307
308 308 User.find(2).destroy
309 309 assert_nil User.find_by_id(2)
310 310 assert_equal User.anonymous, wiki_content.reload.author
311 311 wiki_content.versions.each do |version|
312 312 assert_equal User.anonymous, version.reload.author
313 313 end
314 314 end
315 315
316 316 def test_destroy_should_nullify_issue_categories
317 317 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
318 318
319 319 User.find(2).destroy
320 320 assert_nil User.find_by_id(2)
321 321 assert_nil category.reload.assigned_to_id
322 322 end
323 323
324 324 def test_destroy_should_nullify_changesets
325 325 changeset = Changeset.create!(
326 326 :repository => Repository::Subversion.generate!(
327 327 :project_id => 1
328 328 ),
329 329 :revision => '12',
330 330 :committed_on => Time.now,
331 331 :committer => 'jsmith'
332 332 )
333 333 assert_equal 2, changeset.user_id
334 334
335 335 User.find(2).destroy
336 336 assert_nil User.find_by_id(2)
337 337 assert_nil changeset.reload.user_id
338 338 end
339 339
340 340 def test_anonymous_user_should_not_be_destroyable
341 341 assert_no_difference 'User.count' do
342 342 assert_equal false, User.anonymous.destroy
343 343 end
344 344 end
345 345
346 346 def test_validate_login_presence
347 347 @admin.login = ""
348 348 assert !@admin.save
349 349 assert_equal 1, @admin.errors.count
350 350 end
351 351
352 352 def test_validate_mail_notification_inclusion
353 353 u = User.new
354 354 u.mail_notification = 'foo'
355 355 u.save
356 356 assert_not_nil u.errors[:mail_notification]
357 357 end
358 358
359 359 context "User#try_to_login" do
360 360 should "fall-back to case-insensitive if user login is not found as-typed." do
361 361 user = User.try_to_login("AdMin", "admin")
362 362 assert_kind_of User, user
363 363 assert_equal "admin", user.login
364 364 end
365 365
366 366 should "select the exact matching user first" do
367 367 case_sensitive_user = User.generate_with_protected!(
368 368 :login => 'changed', :password => 'admin',
369 369 :password_confirmation => 'admin')
370 370 # bypass validations to make it appear like existing data
371 371 case_sensitive_user.update_attribute(:login, 'ADMIN')
372 372
373 373 user = User.try_to_login("ADMIN", "admin")
374 374 assert_kind_of User, user
375 375 assert_equal "ADMIN", user.login
376 376
377 377 end
378 378 end
379 379
380 380 def test_password
381 381 user = User.try_to_login("admin", "admin")
382 382 assert_kind_of User, user
383 383 assert_equal "admin", user.login
384 384 user.password = "hello"
385 385 assert user.save
386 386
387 387 user = User.try_to_login("admin", "hello")
388 388 assert_kind_of User, user
389 389 assert_equal "admin", user.login
390 390 end
391 391
392 392 def test_validate_password_length
393 393 with_settings :password_min_length => '100' do
394 394 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
395 395 user.login = "newuser100"
396 396 user.password, user.password_confirmation = "password100", "password100"
397 397 assert !user.save
398 398 assert_equal 1, user.errors.count
399 399 end
400 400 end
401 401
402 402 def test_name_format
403 403 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
404 404 with_settings :user_format => :firstname_lastname do
405 405 assert_equal 'John Smith', @jsmith.reload.name
406 406 end
407 407 with_settings :user_format => :username do
408 408 assert_equal 'jsmith', @jsmith.reload.name
409 409 end
410 410 end
411 411
412 412 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
413 413 with_settings :user_format => 'lastname_coma_firstname' do
414 414 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
415 415 end
416 416 end
417 417
418 418 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
419 419 with_settings :user_format => 'lastname_firstname' do
420 420 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
421 421 end
422 422 end
423 423
424 424 def test_fields_for_order_statement_with_blank_format_should_return_default
425 425 with_settings :user_format => '' do
426 426 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
427 427 end
428 428 end
429 429
430 430 def test_fields_for_order_statement_with_invalid_format_should_return_default
431 431 with_settings :user_format => 'foo' do
432 432 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
433 433 end
434 434 end
435 435
436 436 def test_lock
437 437 user = User.try_to_login("jsmith", "jsmith")
438 438 assert_equal @jsmith, user
439 439
440 440 @jsmith.status = User::STATUS_LOCKED
441 441 assert @jsmith.save
442 442
443 443 user = User.try_to_login("jsmith", "jsmith")
444 444 assert_equal nil, user
445 445 end
446 446
447 447 context ".try_to_login" do
448 448 context "with good credentials" do
449 449 should "return the user" do
450 450 user = User.try_to_login("admin", "admin")
451 451 assert_kind_of User, user
452 452 assert_equal "admin", user.login
453 453 end
454 454 end
455 455
456 456 context "with wrong credentials" do
457 457 should "return nil" do
458 458 assert_nil User.try_to_login("admin", "foo")
459 459 end
460 460 end
461 461 end
462 462
463 463 if ldap_configured?
464 464 context "#try_to_login using LDAP" do
465 465 context "with failed connection to the LDAP server" do
466 466 should "return nil" do
467 467 @auth_source = AuthSourceLdap.find(1)
468 468 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
469 469
470 470 assert_equal nil, User.try_to_login('edavis', 'wrong')
471 471 end
472 472 end
473 473
474 474 context "with an unsuccessful authentication" do
475 475 should "return nil" do
476 476 assert_equal nil, User.try_to_login('edavis', 'wrong')
477 477 end
478 478 end
479 479
480 context "binding with user's account" do
481 setup do
482 @auth_source = AuthSourceLdap.find(1)
483 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
484 @auth_source.account_password = ''
485 @auth_source.save!
486
487 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
488 @ldap_user.login = 'example1'
489 @ldap_user.save!
490 end
491
492 context "with a successful authentication" do
493 should "return the user" do
494 assert_equal @ldap_user, User.try_to_login('example1', '123456')
495 end
496 end
497
498 context "with an unsuccessful authentication" do
499 should "return the user" do
500 assert_nil User.try_to_login('example1', '11111')
501 end
502 end
503 end
504
480 505 context "on the fly registration" do
481 506 setup do
482 507 @auth_source = AuthSourceLdap.find(1)
483 508 @auth_source.update_attribute :onthefly_register, true
484 509 end
485 510
486 511 context "with a successful authentication" do
487 512 should "create a new user account if it doesn't exist" do
488 513 assert_difference('User.count') do
489 514 user = User.try_to_login('edavis', '123456')
490 515 assert !user.admin?
491 516 end
492 517 end
493 518
494 519 should "retrieve existing user" do
495 520 user = User.try_to_login('edavis', '123456')
496 521 user.admin = true
497 522 user.save!
498 523
499 524 assert_no_difference('User.count') do
500 525 user = User.try_to_login('edavis', '123456')
501 526 assert user.admin?
502 527 end
503 528 end
504 529 end
530
531 context "binding with user's account" do
532 setup do
533 @auth_source = AuthSourceLdap.find(1)
534 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
535 @auth_source.account_password = ''
536 @auth_source.save!
537 end
538
539 context "with a successful authentication" do
540 should "create a new user account if it doesn't exist" do
541 assert_difference('User.count') do
542 user = User.try_to_login('example1', '123456')
543 assert_kind_of User, user
544 end
545 end
546 end
547
548 context "with an unsuccessful authentication" do
549 should "return the user" do
550 assert_nil User.try_to_login('example1', '11111')
551 end
552 end
553 end
505 554 end
506 555 end
507 556
508 557 else
509 558 puts "Skipping LDAP tests."
510 559 end
511 560
512 561 def test_create_anonymous
513 562 AnonymousUser.delete_all
514 563 anon = User.anonymous
515 564 assert !anon.new_record?
516 565 assert_kind_of AnonymousUser, anon
517 566 end
518 567
519 568 def test_ensure_single_anonymous_user
520 569 AnonymousUser.delete_all
521 570 anon1 = User.anonymous
522 571 assert !anon1.new_record?
523 572 assert_kind_of AnonymousUser, anon1
524 573 anon2 = AnonymousUser.create(
525 574 :lastname => 'Anonymous', :firstname => '',
526 575 :mail => '', :login => '', :status => 0)
527 576 assert_equal 1, anon2.errors.count
528 577 end
529 578
530 579 def test_rss_key
531 580 assert_nil @jsmith.rss_token
532 581 key = @jsmith.rss_key
533 582 assert_equal 40, key.length
534 583
535 584 @jsmith.reload
536 585 assert_equal key, @jsmith.rss_key
537 586 end
538 587
539 588 context "User#api_key" do
540 589 should "generate a new one if the user doesn't have one" do
541 590 user = User.generate_with_protected!(:api_token => nil)
542 591 assert_nil user.api_token
543 592
544 593 key = user.api_key
545 594 assert_equal 40, key.length
546 595 user.reload
547 596 assert_equal key, user.api_key
548 597 end
549 598
550 599 should "return the existing api token value" do
551 600 user = User.generate_with_protected!
552 601 token = Token.generate!(:action => 'api')
553 602 user.api_token = token
554 603 assert user.save
555 604
556 605 assert_equal token.value, user.api_key
557 606 end
558 607 end
559 608
560 609 context "User#find_by_api_key" do
561 610 should "return nil if no matching key is found" do
562 611 assert_nil User.find_by_api_key('zzzzzzzzz')
563 612 end
564 613
565 614 should "return nil if the key is found for an inactive user" do
566 615 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
567 616 token = Token.generate!(:action => 'api')
568 617 user.api_token = token
569 618 user.save
570 619
571 620 assert_nil User.find_by_api_key(token.value)
572 621 end
573 622
574 623 should "return the user if the key is found for an active user" do
575 624 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
576 625 token = Token.generate!(:action => 'api')
577 626 user.api_token = token
578 627 user.save
579 628
580 629 assert_equal user, User.find_by_api_key(token.value)
581 630 end
582 631 end
583 632
584 633 def test_roles_for_project
585 634 # user with a role
586 635 roles = @jsmith.roles_for_project(Project.find(1))
587 636 assert_kind_of Role, roles.first
588 637 assert_equal "Manager", roles.first.name
589 638
590 639 # user with no role
591 640 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
592 641 end
593 642
594 643 def test_projects_by_role_for_user_with_role
595 644 user = User.find(2)
596 645 assert_kind_of Hash, user.projects_by_role
597 646 assert_equal 2, user.projects_by_role.size
598 647 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
599 648 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
600 649 end
601 650
602 651 def test_projects_by_role_for_user_with_no_role
603 652 user = User.generate!
604 653 assert_equal({}, user.projects_by_role)
605 654 end
606 655
607 656 def test_projects_by_role_for_anonymous
608 657 assert_equal({}, User.anonymous.projects_by_role)
609 658 end
610 659
611 660 def test_valid_notification_options
612 661 # without memberships
613 662 assert_equal 5, User.find(7).valid_notification_options.size
614 663 # with memberships
615 664 assert_equal 6, User.find(2).valid_notification_options.size
616 665 end
617 666
618 667 def test_valid_notification_options_class_method
619 668 assert_equal 5, User.valid_notification_options.size
620 669 assert_equal 5, User.valid_notification_options(User.find(7)).size
621 670 assert_equal 6, User.valid_notification_options(User.find(2)).size
622 671 end
623 672
624 673 def test_mail_notification_all
625 674 @jsmith.mail_notification = 'all'
626 675 @jsmith.notified_project_ids = []
627 676 @jsmith.save
628 677 @jsmith.reload
629 678 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
630 679 end
631 680
632 681 def test_mail_notification_selected
633 682 @jsmith.mail_notification = 'selected'
634 683 @jsmith.notified_project_ids = [1]
635 684 @jsmith.save
636 685 @jsmith.reload
637 686 assert Project.find(1).recipients.include?(@jsmith.mail)
638 687 end
639 688
640 689 def test_mail_notification_only_my_events
641 690 @jsmith.mail_notification = 'only_my_events'
642 691 @jsmith.notified_project_ids = []
643 692 @jsmith.save
644 693 @jsmith.reload
645 694 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
646 695 end
647 696
648 697 def test_comments_sorting_preference
649 698 assert !@jsmith.wants_comments_in_reverse_order?
650 699 @jsmith.pref.comments_sorting = 'asc'
651 700 assert !@jsmith.wants_comments_in_reverse_order?
652 701 @jsmith.pref.comments_sorting = 'desc'
653 702 assert @jsmith.wants_comments_in_reverse_order?
654 703 end
655 704
656 705 def test_find_by_mail_should_be_case_insensitive
657 706 u = User.find_by_mail('JSmith@somenet.foo')
658 707 assert_not_nil u
659 708 assert_equal 'jsmith@somenet.foo', u.mail
660 709 end
661 710
662 711 def test_random_password
663 712 u = User.new
664 713 u.random_password
665 714 assert !u.password.blank?
666 715 assert !u.password_confirmation.blank?
667 716 end
668 717
669 718 context "#change_password_allowed?" do
670 719 should "be allowed if no auth source is set" do
671 720 user = User.generate_with_protected!
672 721 assert user.change_password_allowed?
673 722 end
674 723
675 724 should "delegate to the auth source" do
676 725 user = User.generate_with_protected!
677 726
678 727 allowed_auth_source = AuthSource.generate!
679 728 def allowed_auth_source.allow_password_changes?; true; end
680 729
681 730 denied_auth_source = AuthSource.generate!
682 731 def denied_auth_source.allow_password_changes?; false; end
683 732
684 733 assert user.change_password_allowed?
685 734
686 735 user.auth_source = allowed_auth_source
687 736 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
688 737
689 738 user.auth_source = denied_auth_source
690 739 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
691 740 end
692 741
693 742 end
694 743
695 744 context "#allowed_to?" do
696 745 context "with a unique project" do
697 746 should "return false if project is archived" do
698 747 project = Project.find(1)
699 748 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
700 749 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
701 750 end
702 751
703 752 should "return false if related module is disabled" do
704 753 project = Project.find(1)
705 754 project.enabled_module_names = ["issue_tracking"]
706 755 assert @admin.allowed_to?(:add_issues, project)
707 756 assert ! @admin.allowed_to?(:view_wiki_pages, project)
708 757 end
709 758
710 759 should "authorize nearly everything for admin users" do
711 760 project = Project.find(1)
712 761 assert ! @admin.member_of?(project)
713 762 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
714 763 assert @admin.allowed_to?(p.to_sym, project)
715 764 end
716 765 end
717 766
718 767 should "authorize normal users depending on their roles" do
719 768 project = Project.find(1)
720 769 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
721 770 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
722 771 end
723 772 end
724 773
725 774 context "with multiple projects" do
726 775 should "return false if array is empty" do
727 776 assert ! @admin.allowed_to?(:view_project, [])
728 777 end
729 778
730 779 should "return true only if user has permission on all these projects" do
731 780 assert @admin.allowed_to?(:view_project, Project.all)
732 781 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
733 782 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
734 783 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
735 784 end
736 785
737 786 should "behave correctly with arrays of 1 project" do
738 787 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
739 788 end
740 789 end
741 790
742 791 context "with options[:global]" do
743 792 should "authorize if user has at least one role that has this permission" do
744 793 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
745 794 @anonymous = User.find(6)
746 795 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
747 796 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
748 797 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
749 798 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
750 799 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
751 800 end
752 801 end
753 802 end
754 803
755 804 context "User#notify_about?" do
756 805 context "Issues" do
757 806 setup do
758 807 @project = Project.find(1)
759 808 @author = User.generate_with_protected!
760 809 @assignee = User.generate_with_protected!
761 810 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
762 811 end
763 812
764 813 should "be true for a user with :all" do
765 814 @author.update_attribute(:mail_notification, 'all')
766 815 assert @author.notify_about?(@issue)
767 816 end
768 817
769 818 should "be false for a user with :none" do
770 819 @author.update_attribute(:mail_notification, 'none')
771 820 assert ! @author.notify_about?(@issue)
772 821 end
773 822
774 823 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
775 824 @user = User.generate_with_protected!(:mail_notification => 'only_my_events')
776 825 Member.create!(:user => @user, :project => @project, :role_ids => [1])
777 826 assert ! @user.notify_about?(@issue)
778 827 end
779 828
780 829 should "be true for a user with :only_my_events and is the author" do
781 830 @author.update_attribute(:mail_notification, 'only_my_events')
782 831 assert @author.notify_about?(@issue)
783 832 end
784 833
785 834 should "be true for a user with :only_my_events and is the assignee" do
786 835 @assignee.update_attribute(:mail_notification, 'only_my_events')
787 836 assert @assignee.notify_about?(@issue)
788 837 end
789 838
790 839 should "be true for a user with :only_assigned and is the assignee" do
791 840 @assignee.update_attribute(:mail_notification, 'only_assigned')
792 841 assert @assignee.notify_about?(@issue)
793 842 end
794 843
795 844 should "be false for a user with :only_assigned and is not the assignee" do
796 845 @author.update_attribute(:mail_notification, 'only_assigned')
797 846 assert ! @author.notify_about?(@issue)
798 847 end
799 848
800 849 should "be true for a user with :only_owner and is the author" do
801 850 @author.update_attribute(:mail_notification, 'only_owner')
802 851 assert @author.notify_about?(@issue)
803 852 end
804 853
805 854 should "be false for a user with :only_owner and is not the author" do
806 855 @assignee.update_attribute(:mail_notification, 'only_owner')
807 856 assert ! @assignee.notify_about?(@issue)
808 857 end
809 858
810 859 should "be true for a user with :selected and is the author" do
811 860 @author.update_attribute(:mail_notification, 'selected')
812 861 assert @author.notify_about?(@issue)
813 862 end
814 863
815 864 should "be true for a user with :selected and is the assignee" do
816 865 @assignee.update_attribute(:mail_notification, 'selected')
817 866 assert @assignee.notify_about?(@issue)
818 867 end
819 868
820 869 should "be false for a user with :selected and is not the author or assignee" do
821 870 @user = User.generate_with_protected!(:mail_notification => 'selected')
822 871 Member.create!(:user => @user, :project => @project, :role_ids => [1])
823 872 assert ! @user.notify_about?(@issue)
824 873 end
825 874 end
826 875
827 876 context "other events" do
828 877 should 'be added and tested'
829 878 end
830 879 end
831 880
832 881 def test_salt_unsalted_passwords
833 882 # Restore a user with an unsalted password
834 883 user = User.find(1)
835 884 user.salt = nil
836 885 user.hashed_password = User.hash_password("unsalted")
837 886 user.save!
838 887
839 888 User.salt_unsalted_passwords!
840 889
841 890 user.reload
842 891 # Salt added
843 892 assert !user.salt.blank?
844 893 # Password still valid
845 894 assert user.check_password?("unsalted")
846 895 assert_equal user, User.try_to_login(user.login, "unsalted")
847 896 end
848 897
849 898 if Object.const_defined?(:OpenID)
850 899
851 900 def test_setting_identity_url
852 901 normalized_open_id_url = 'http://example.com/'
853 902 u = User.new( :identity_url => 'http://example.com/' )
854 903 assert_equal normalized_open_id_url, u.identity_url
855 904 end
856 905
857 906 def test_setting_identity_url_without_trailing_slash
858 907 normalized_open_id_url = 'http://example.com/'
859 908 u = User.new( :identity_url => 'http://example.com' )
860 909 assert_equal normalized_open_id_url, u.identity_url
861 910 end
862 911
863 912 def test_setting_identity_url_without_protocol
864 913 normalized_open_id_url = 'http://example.com/'
865 914 u = User.new( :identity_url => 'example.com' )
866 915 assert_equal normalized_open_id_url, u.identity_url
867 916 end
868 917
869 918 def test_setting_blank_identity_url
870 919 u = User.new( :identity_url => 'example.com' )
871 920 u.identity_url = ''
872 921 assert u.identity_url.blank?
873 922 end
874 923
875 924 def test_setting_invalid_identity_url
876 925 u = User.new( :identity_url => 'this is not an openid url' )
877 926 assert u.identity_url.blank?
878 927 end
879 928
880 929 else
881 930 puts "Skipping openid tests."
882 931 end
883 932
884 933 end
General Comments 0
You need to be logged in to leave comments. Login now