##// END OF EJS Templates
Make 2nd parameter optional in User#allowed_to_globally? for consistency (#6498)....
Jean-Baptiste Barth -
r13010:d00f875800fc
parent child
Show More
@@ -1,762 +1,766
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 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 "digest/sha1"
19 19
20 20 class User < Principal
21 21 include Redmine::SafeAttributes
22 22
23 23 # Different ways of displaying/sorting users
24 24 USER_FORMATS = {
25 25 :firstname_lastname => {
26 26 :string => '#{firstname} #{lastname}',
27 27 :order => %w(firstname lastname id),
28 28 :setting_order => 1
29 29 },
30 30 :firstname_lastinitial => {
31 31 :string => '#{firstname} #{lastname.to_s.chars.first}.',
32 32 :order => %w(firstname lastname id),
33 33 :setting_order => 2
34 34 },
35 35 :firstinitial_lastname => {
36 36 :string => '#{firstname.to_s.gsub(/(([[:alpha:]])[[:alpha:]]*\.?)/, \'\2.\')} #{lastname}',
37 37 :order => %w(firstname lastname id),
38 38 :setting_order => 2
39 39 },
40 40 :firstname => {
41 41 :string => '#{firstname}',
42 42 :order => %w(firstname id),
43 43 :setting_order => 3
44 44 },
45 45 :lastname_firstname => {
46 46 :string => '#{lastname} #{firstname}',
47 47 :order => %w(lastname firstname id),
48 48 :setting_order => 4
49 49 },
50 50 :lastname_coma_firstname => {
51 51 :string => '#{lastname}, #{firstname}',
52 52 :order => %w(lastname firstname id),
53 53 :setting_order => 5
54 54 },
55 55 :lastname => {
56 56 :string => '#{lastname}',
57 57 :order => %w(lastname id),
58 58 :setting_order => 6
59 59 },
60 60 :username => {
61 61 :string => '#{login}',
62 62 :order => %w(login id),
63 63 :setting_order => 7
64 64 },
65 65 }
66 66
67 67 MAIL_NOTIFICATION_OPTIONS = [
68 68 ['all', :label_user_mail_option_all],
69 69 ['selected', :label_user_mail_option_selected],
70 70 ['only_my_events', :label_user_mail_option_only_my_events],
71 71 ['only_assigned', :label_user_mail_option_only_assigned],
72 72 ['only_owner', :label_user_mail_option_only_owner],
73 73 ['none', :label_user_mail_option_none]
74 74 ]
75 75
76 76 has_and_belongs_to_many :groups,
77 77 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
78 78 :after_add => Proc.new {|user, group| group.user_added(user)},
79 79 :after_remove => Proc.new {|user, group| group.user_removed(user)}
80 80 has_many :changesets, :dependent => :nullify
81 81 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
82 82 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
83 83 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
84 84 belongs_to :auth_source
85 85
86 86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
87 87 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
88 88
89 89 acts_as_customizable
90 90
91 91 attr_accessor :password, :password_confirmation, :generate_password
92 92 attr_accessor :last_before_login_on
93 93 # Prevents unauthorized assignments
94 94 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
95 95
96 96 LOGIN_LENGTH_LIMIT = 60
97 97 MAIL_LENGTH_LIMIT = 60
98 98
99 99 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
100 100 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
101 101 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
102 102 # Login must contain letters, numbers, underscores only
103 103 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
104 104 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
105 105 validates_length_of :firstname, :lastname, :maximum => 30
106 106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
107 107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
108 108 validates_confirmation_of :password, :allow_nil => true
109 109 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
110 110 validate :validate_password_length
111 111
112 112 before_create :set_mail_notification
113 113 before_save :generate_password_if_needed, :update_hashed_password
114 114 before_destroy :remove_references_before_destroy
115 115 after_save :update_notified_project_ids
116 116
117 117 scope :in_group, lambda {|group|
118 118 group_id = group.is_a?(Group) ? group.id : group.to_i
119 119 where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
120 120 }
121 121 scope :not_in_group, lambda {|group|
122 122 group_id = group.is_a?(Group) ? group.id : group.to_i
123 123 where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
124 124 }
125 125 scope :sorted, lambda { order(*User.fields_for_order_statement)}
126 126
127 127 def set_mail_notification
128 128 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
129 129 true
130 130 end
131 131
132 132 def update_hashed_password
133 133 # update hashed_password if password was set
134 134 if self.password && self.auth_source_id.blank?
135 135 salt_password(password)
136 136 end
137 137 end
138 138
139 139 alias :base_reload :reload
140 140 def reload(*args)
141 141 @name = nil
142 142 @projects_by_role = nil
143 143 @membership_by_project_id = nil
144 144 @notified_projects_ids = nil
145 145 @notified_projects_ids_changed = false
146 146 @builtin_role = nil
147 147 base_reload(*args)
148 148 end
149 149
150 150 def mail=(arg)
151 151 write_attribute(:mail, arg.to_s.strip)
152 152 end
153 153
154 154 def identity_url=(url)
155 155 if url.blank?
156 156 write_attribute(:identity_url, '')
157 157 else
158 158 begin
159 159 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
160 160 rescue OpenIdAuthentication::InvalidOpenId
161 161 # Invalid url, don't save
162 162 end
163 163 end
164 164 self.read_attribute(:identity_url)
165 165 end
166 166
167 167 # Returns the user that matches provided login and password, or nil
168 168 def self.try_to_login(login, password, active_only=true)
169 169 login = login.to_s
170 170 password = password.to_s
171 171
172 172 # Make sure no one can sign in with an empty login or password
173 173 return nil if login.empty? || password.empty?
174 174 user = find_by_login(login)
175 175 if user
176 176 # user is already in local database
177 177 return nil unless user.check_password?(password)
178 178 return nil if !user.active? && active_only
179 179 else
180 180 # user is not yet registered, try to authenticate with available sources
181 181 attrs = AuthSource.authenticate(login, password)
182 182 if attrs
183 183 user = new(attrs)
184 184 user.login = login
185 185 user.language = Setting.default_language
186 186 if user.save
187 187 user.reload
188 188 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
189 189 end
190 190 end
191 191 end
192 192 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
193 193 user
194 194 rescue => text
195 195 raise text
196 196 end
197 197
198 198 # Returns the user who matches the given autologin +key+ or nil
199 199 def self.try_to_autologin(key)
200 200 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
201 201 if user
202 202 user.update_column(:last_login_on, Time.now)
203 203 user
204 204 end
205 205 end
206 206
207 207 def self.name_formatter(formatter = nil)
208 208 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
209 209 end
210 210
211 211 # Returns an array of fields names than can be used to make an order statement for users
212 212 # according to how user names are displayed
213 213 # Examples:
214 214 #
215 215 # User.fields_for_order_statement => ['users.login', 'users.id']
216 216 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
217 217 def self.fields_for_order_statement(table=nil)
218 218 table ||= table_name
219 219 name_formatter[:order].map {|field| "#{table}.#{field}"}
220 220 end
221 221
222 222 # Return user's full name for display
223 223 def name(formatter = nil)
224 224 f = self.class.name_formatter(formatter)
225 225 if formatter
226 226 eval('"' + f[:string] + '"')
227 227 else
228 228 @name ||= eval('"' + f[:string] + '"')
229 229 end
230 230 end
231 231
232 232 def active?
233 233 self.status == STATUS_ACTIVE
234 234 end
235 235
236 236 def registered?
237 237 self.status == STATUS_REGISTERED
238 238 end
239 239
240 240 def locked?
241 241 self.status == STATUS_LOCKED
242 242 end
243 243
244 244 def activate
245 245 self.status = STATUS_ACTIVE
246 246 end
247 247
248 248 def register
249 249 self.status = STATUS_REGISTERED
250 250 end
251 251
252 252 def lock
253 253 self.status = STATUS_LOCKED
254 254 end
255 255
256 256 def activate!
257 257 update_attribute(:status, STATUS_ACTIVE)
258 258 end
259 259
260 260 def register!
261 261 update_attribute(:status, STATUS_REGISTERED)
262 262 end
263 263
264 264 def lock!
265 265 update_attribute(:status, STATUS_LOCKED)
266 266 end
267 267
268 268 # Returns true if +clear_password+ is the correct user's password, otherwise false
269 269 def check_password?(clear_password)
270 270 if auth_source_id.present?
271 271 auth_source.authenticate(self.login, clear_password)
272 272 else
273 273 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
274 274 end
275 275 end
276 276
277 277 # Generates a random salt and computes hashed_password for +clear_password+
278 278 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
279 279 def salt_password(clear_password)
280 280 self.salt = User.generate_salt
281 281 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
282 282 end
283 283
284 284 # Does the backend storage allow this user to change their password?
285 285 def change_password_allowed?
286 286 return true if auth_source.nil?
287 287 return auth_source.allow_password_changes?
288 288 end
289 289
290 290 def must_change_password?
291 291 must_change_passwd? && change_password_allowed?
292 292 end
293 293
294 294 def generate_password?
295 295 generate_password == '1' || generate_password == true
296 296 end
297 297
298 298 # Generate and set a random password on given length
299 299 def random_password(length=40)
300 300 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
301 301 chars -= %w(0 O 1 l)
302 302 password = ''
303 303 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
304 304 self.password = password
305 305 self.password_confirmation = password
306 306 self
307 307 end
308 308
309 309 def pref
310 310 self.preference ||= UserPreference.new(:user => self)
311 311 end
312 312
313 313 def time_zone
314 314 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
315 315 end
316 316
317 317 def force_default_language?
318 318 Setting.force_default_language_for_loggedin?
319 319 end
320 320
321 321 def language
322 322 if force_default_language?
323 323 Setting.default_language
324 324 else
325 325 super
326 326 end
327 327 end
328 328
329 329 def wants_comments_in_reverse_order?
330 330 self.pref[:comments_sorting] == 'desc'
331 331 end
332 332
333 333 # Return user's RSS key (a 40 chars long string), used to access feeds
334 334 def rss_key
335 335 if rss_token.nil?
336 336 create_rss_token(:action => 'feeds')
337 337 end
338 338 rss_token.value
339 339 end
340 340
341 341 # Return user's API key (a 40 chars long string), used to access the API
342 342 def api_key
343 343 if api_token.nil?
344 344 create_api_token(:action => 'api')
345 345 end
346 346 api_token.value
347 347 end
348 348
349 349 # Return an array of project ids for which the user has explicitly turned mail notifications on
350 350 def notified_projects_ids
351 351 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
352 352 end
353 353
354 354 def notified_project_ids=(ids)
355 355 @notified_projects_ids_changed = true
356 356 @notified_projects_ids = ids
357 357 end
358 358
359 359 # Updates per project notifications (after_save callback)
360 360 def update_notified_project_ids
361 361 if @notified_projects_ids_changed
362 362 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
363 363 members.update_all(:mail_notification => false)
364 364 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
365 365 end
366 366 end
367 367 private :update_notified_project_ids
368 368
369 369 def valid_notification_options
370 370 self.class.valid_notification_options(self)
371 371 end
372 372
373 373 # Only users that belong to more than 1 project can select projects for which they are notified
374 374 def self.valid_notification_options(user=nil)
375 375 # Note that @user.membership.size would fail since AR ignores
376 376 # :include association option when doing a count
377 377 if user.nil? || user.memberships.length < 1
378 378 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
379 379 else
380 380 MAIL_NOTIFICATION_OPTIONS
381 381 end
382 382 end
383 383
384 384 # Find a user account by matching the exact login and then a case-insensitive
385 385 # version. Exact matches will be given priority.
386 386 def self.find_by_login(login)
387 387 login = Redmine::CodesetUtil.replace_invalid_utf8(login.to_s)
388 388 if login.present?
389 389 # First look for an exact match
390 390 user = where(:login => login).detect {|u| u.login == login}
391 391 unless user
392 392 # Fail over to case-insensitive if none was found
393 393 user = where("LOWER(login) = ?", login.downcase).first
394 394 end
395 395 user
396 396 end
397 397 end
398 398
399 399 def self.find_by_rss_key(key)
400 400 Token.find_active_user('feeds', key)
401 401 end
402 402
403 403 def self.find_by_api_key(key)
404 404 Token.find_active_user('api', key)
405 405 end
406 406
407 407 # Makes find_by_mail case-insensitive
408 408 def self.find_by_mail(mail)
409 409 where("LOWER(mail) = ?", mail.to_s.downcase).first
410 410 end
411 411
412 412 # Returns true if the default admin account can no longer be used
413 413 def self.default_admin_account_changed?
414 414 !User.active.find_by_login("admin").try(:check_password?, "admin")
415 415 end
416 416
417 417 def to_s
418 418 name
419 419 end
420 420
421 421 CSS_CLASS_BY_STATUS = {
422 422 STATUS_ANONYMOUS => 'anon',
423 423 STATUS_ACTIVE => 'active',
424 424 STATUS_REGISTERED => 'registered',
425 425 STATUS_LOCKED => 'locked'
426 426 }
427 427
428 428 def css_classes
429 429 "user #{CSS_CLASS_BY_STATUS[status]}"
430 430 end
431 431
432 432 # Returns the current day according to user's time zone
433 433 def today
434 434 if time_zone.nil?
435 435 Date.today
436 436 else
437 437 Time.now.in_time_zone(time_zone).to_date
438 438 end
439 439 end
440 440
441 441 # Returns the day of +time+ according to user's time zone
442 442 def time_to_date(time)
443 443 if time_zone.nil?
444 444 time.to_date
445 445 else
446 446 time.in_time_zone(time_zone).to_date
447 447 end
448 448 end
449 449
450 450 def logged?
451 451 true
452 452 end
453 453
454 454 def anonymous?
455 455 !logged?
456 456 end
457 457
458 458 # Returns user's membership for the given project
459 459 # or nil if the user is not a member of project
460 460 def membership(project)
461 461 project_id = project.is_a?(Project) ? project.id : project
462 462
463 463 @membership_by_project_id ||= Hash.new {|h, project_id|
464 464 h[project_id] = memberships.where(:project_id => project_id).first
465 465 }
466 466 @membership_by_project_id[project_id]
467 467 end
468 468
469 469 # Returns the user's bult-in role
470 470 def builtin_role
471 471 @builtin_role ||= Role.non_member
472 472 end
473 473
474 474 # Return user's roles for project
475 475 def roles_for_project(project)
476 476 roles = []
477 477 # No role on archived projects
478 478 return roles if project.nil? || project.archived?
479 479 if membership = membership(project)
480 480 roles = membership.roles
481 481 else
482 482 roles << builtin_role
483 483 end
484 484 roles
485 485 end
486 486
487 487 # Return true if the user is a member of project
488 488 def member_of?(project)
489 489 projects.to_a.include?(project)
490 490 end
491 491
492 492 # Returns a hash of user's projects grouped by roles
493 493 def projects_by_role
494 494 return @projects_by_role if @projects_by_role
495 495
496 496 @projects_by_role = Hash.new([])
497 497 memberships.each do |membership|
498 498 if membership.project
499 499 membership.roles.each do |role|
500 500 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
501 501 @projects_by_role[role] << membership.project
502 502 end
503 503 end
504 504 end
505 505 @projects_by_role.each do |role, projects|
506 506 projects.uniq!
507 507 end
508 508
509 509 @projects_by_role
510 510 end
511 511
512 512 # Returns true if user is arg or belongs to arg
513 513 def is_or_belongs_to?(arg)
514 514 if arg.is_a?(User)
515 515 self == arg
516 516 elsif arg.is_a?(Group)
517 517 arg.users.include?(self)
518 518 else
519 519 false
520 520 end
521 521 end
522 522
523 523 # Return true if the user is allowed to do the specified action on a specific context
524 524 # Action can be:
525 525 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
526 526 # * a permission Symbol (eg. :edit_project)
527 527 # Context can be:
528 528 # * a project : returns true if user is allowed to do the specified action on this project
529 529 # * an array of projects : returns true if user is allowed on every project
530 530 # * nil with options[:global] set : check if user has at least one role allowed for this action,
531 531 # or falls back to Non Member / Anonymous permissions depending if the user is logged
532 532 def allowed_to?(action, context, options={}, &block)
533 533 if context && context.is_a?(Project)
534 534 return false unless context.allows_to?(action)
535 535 # Admin users are authorized for anything else
536 536 return true if admin?
537 537
538 538 roles = roles_for_project(context)
539 539 return false unless roles
540 540 roles.any? {|role|
541 541 (context.is_public? || role.member?) &&
542 542 role.allowed_to?(action) &&
543 543 (block_given? ? yield(role, self) : true)
544 544 }
545 545 elsif context && context.is_a?(Array)
546 546 if context.empty?
547 547 false
548 548 else
549 549 # Authorize if user is authorized on every element of the array
550 550 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
551 551 end
552 552 elsif options[:global]
553 553 # Admin users are always authorized
554 554 return true if admin?
555 555
556 556 # authorize if user has at least one role that has this permission
557 557 roles = memberships.collect {|m| m.roles}.flatten.uniq
558 558 roles << (self.logged? ? Role.non_member : Role.anonymous)
559 559 roles.any? {|role|
560 560 role.allowed_to?(action) &&
561 561 (block_given? ? yield(role, self) : true)
562 562 }
563 563 else
564 564 false
565 565 end
566 566 end
567 567
568 568 # Is the user allowed to do the specified action on any project?
569 569 # See allowed_to? for the actions and valid options.
570 def allowed_to_globally?(action, options, &block)
570 #
571 # NB: this method is not used anywhere in the core codebase as of
572 # 2.5.2, but it's used by many plugins so if we ever want to remove
573 # it it has to be carefully deprecated for a version or two.
574 def allowed_to_globally?(action, options={}, &block)
571 575 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
572 576 end
573 577
574 578 # Returns true if the user is allowed to delete the user's own account
575 579 def own_account_deletable?
576 580 Setting.unsubscribe? &&
577 581 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
578 582 end
579 583
580 584 safe_attributes 'login',
581 585 'firstname',
582 586 'lastname',
583 587 'mail',
584 588 'mail_notification',
585 589 'notified_project_ids',
586 590 'language',
587 591 'custom_field_values',
588 592 'custom_fields',
589 593 'identity_url'
590 594
591 595 safe_attributes 'status',
592 596 'auth_source_id',
593 597 'generate_password',
594 598 'must_change_passwd',
595 599 :if => lambda {|user, current_user| current_user.admin?}
596 600
597 601 safe_attributes 'group_ids',
598 602 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
599 603
600 604 # Utility method to help check if a user should be notified about an
601 605 # event.
602 606 #
603 607 # TODO: only supports Issue events currently
604 608 def notify_about?(object)
605 609 if mail_notification == 'all'
606 610 true
607 611 elsif mail_notification.blank? || mail_notification == 'none'
608 612 false
609 613 else
610 614 case object
611 615 when Issue
612 616 case mail_notification
613 617 when 'selected', 'only_my_events'
614 618 # user receives notifications for created/assigned issues on unselected projects
615 619 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
616 620 when 'only_assigned'
617 621 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
618 622 when 'only_owner'
619 623 object.author == self
620 624 end
621 625 when News
622 626 # always send to project members except when mail_notification is set to 'none'
623 627 true
624 628 end
625 629 end
626 630 end
627 631
628 632 def self.current=(user)
629 633 RequestStore.store[:current_user] = user
630 634 end
631 635
632 636 def self.current
633 637 RequestStore.store[:current_user] ||= User.anonymous
634 638 end
635 639
636 640 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
637 641 # one anonymous user per database.
638 642 def self.anonymous
639 643 anonymous_user = AnonymousUser.first
640 644 if anonymous_user.nil?
641 645 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
642 646 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
643 647 end
644 648 anonymous_user
645 649 end
646 650
647 651 # Salts all existing unsalted passwords
648 652 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
649 653 # This method is used in the SaltPasswords migration and is to be kept as is
650 654 def self.salt_unsalted_passwords!
651 655 transaction do
652 656 User.where("salt IS NULL OR salt = ''").find_each do |user|
653 657 next if user.hashed_password.blank?
654 658 salt = User.generate_salt
655 659 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
656 660 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
657 661 end
658 662 end
659 663 end
660 664
661 665 protected
662 666
663 667 def validate_password_length
664 668 return if password.blank? && generate_password?
665 669 # Password length validation based on setting
666 670 if !password.nil? && password.size < Setting.password_min_length.to_i
667 671 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
668 672 end
669 673 end
670 674
671 675 private
672 676
673 677 def generate_password_if_needed
674 678 if generate_password? && auth_source.nil?
675 679 length = [Setting.password_min_length.to_i + 2, 10].max
676 680 random_password(length)
677 681 end
678 682 end
679 683
680 684 # Removes references that are not handled by associations
681 685 # Things that are not deleted are reassociated with the anonymous user
682 686 def remove_references_before_destroy
683 687 return if self.id.nil?
684 688
685 689 substitute = User.anonymous
686 690 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
687 691 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
688 692 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
689 693 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
690 694 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
691 695 JournalDetail.
692 696 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
693 697 update_all(['old_value = ?', substitute.id.to_s])
694 698 JournalDetail.
695 699 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
696 700 update_all(['value = ?', substitute.id.to_s])
697 701 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
698 702 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
699 703 # Remove private queries and keep public ones
700 704 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
701 705 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
702 706 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
703 707 Token.delete_all ['user_id = ?', id]
704 708 Watcher.delete_all ['user_id = ?', id]
705 709 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
706 710 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
707 711 end
708 712
709 713 # Return password digest
710 714 def self.hash_password(clear_password)
711 715 Digest::SHA1.hexdigest(clear_password || "")
712 716 end
713 717
714 718 # Returns a 128bits random salt as a hex string (32 chars long)
715 719 def self.generate_salt
716 720 Redmine::Utils.random_hex(16)
717 721 end
718 722
719 723 end
720 724
721 725 class AnonymousUser < User
722 726 validate :validate_anonymous_uniqueness, :on => :create
723 727
724 728 def validate_anonymous_uniqueness
725 729 # There should be only one AnonymousUser in the database
726 730 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
727 731 end
728 732
729 733 def available_custom_fields
730 734 []
731 735 end
732 736
733 737 # Overrides a few properties
734 738 def logged?; false end
735 739 def admin; false end
736 740 def name(*args); I18n.t(:label_user_anonymous) end
737 741 def mail; nil end
738 742 def time_zone; nil end
739 743 def rss_key; nil end
740 744
741 745 def pref
742 746 UserPreference.new(:user => self)
743 747 end
744 748
745 749 # Returns the user's bult-in role
746 750 def builtin_role
747 751 @builtin_role ||= Role.anonymous
748 752 end
749 753
750 754 def membership(*args)
751 755 nil
752 756 end
753 757
754 758 def member_of?(*args)
755 759 false
756 760 end
757 761
758 762 # Anonymous user can not be destroyed
759 763 def destroy
760 764 false
761 765 end
762 766 end
@@ -1,1154 +1,1167
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2014 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
30 30 def setup
31 31 @admin = User.find(1)
32 32 @jsmith = User.find(2)
33 33 @dlopper = User.find(3)
34 34 end
35 35
36 36 def test_sorted_scope_should_sort_user_by_display_name
37 37 assert_equal User.all.map(&:name).map(&:downcase).sort,
38 38 User.sorted.map(&:name).map(&:downcase)
39 39 end
40 40
41 41 def test_generate
42 42 User.generate!(:firstname => 'Testing connection')
43 43 User.generate!(:firstname => 'Testing connection')
44 44 assert_equal 2, User.where(:firstname => 'Testing connection').count
45 45 end
46 46
47 47 def test_truth
48 48 assert_kind_of User, @jsmith
49 49 end
50 50
51 51 def test_mail_should_be_stripped
52 52 u = User.new
53 53 u.mail = " foo@bar.com "
54 54 assert_equal "foo@bar.com", u.mail
55 55 end
56 56
57 57 def test_mail_validation
58 58 u = User.new
59 59 u.mail = ''
60 60 assert !u.valid?
61 61 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
62 62 end
63 63
64 64 def test_login_length_validation
65 65 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
66 66 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
67 67 assert !user.valid?
68 68
69 69 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
70 70 assert user.valid?
71 71 assert user.save
72 72 end
73 73
74 74 def test_generate_password_should_respect_minimum_password_length
75 75 with_settings :password_min_length => 15 do
76 76 user = User.generate!(:generate_password => true)
77 77 assert user.password.length >= 15
78 78 end
79 79 end
80 80
81 81 def test_generate_password_should_not_generate_password_with_less_than_10_characters
82 82 with_settings :password_min_length => 4 do
83 83 user = User.generate!(:generate_password => true)
84 84 assert user.password.length >= 10
85 85 end
86 86 end
87 87
88 88 def test_generate_password_on_create_should_set_password
89 89 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
90 90 user.login = "newuser"
91 91 user.generate_password = true
92 92 assert user.save
93 93
94 94 password = user.password
95 95 assert user.check_password?(password)
96 96 end
97 97
98 98 def test_generate_password_on_update_should_update_password
99 99 user = User.find(2)
100 100 hash = user.hashed_password
101 101 user.generate_password = true
102 102 assert user.save
103 103
104 104 password = user.password
105 105 assert user.check_password?(password)
106 106 assert_not_equal hash, user.reload.hashed_password
107 107 end
108 108
109 109 def test_create
110 110 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
111 111
112 112 user.login = "jsmith"
113 113 user.password, user.password_confirmation = "password", "password"
114 114 # login uniqueness
115 115 assert !user.save
116 116 assert_equal 1, user.errors.count
117 117
118 118 user.login = "newuser"
119 119 user.password, user.password_confirmation = "password", "pass"
120 120 # password confirmation
121 121 assert !user.save
122 122 assert_equal 1, user.errors.count
123 123
124 124 user.password, user.password_confirmation = "password", "password"
125 125 assert user.save
126 126 end
127 127
128 128 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
129 129 @user1 = User.generate!
130 130 assert_equal 'only_my_events', @user1.mail_notification
131 131 with_settings :default_notification_option => 'all' do
132 132 @user2 = User.generate!
133 133 assert_equal 'all', @user2.mail_notification
134 134 end
135 135 end
136 136
137 137 def test_user_login_should_be_case_insensitive
138 138 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
139 139 u.login = 'newuser'
140 140 u.password, u.password_confirmation = "password", "password"
141 141 assert u.save
142 142 u = User.new(:firstname => "Similar", :lastname => "User",
143 143 :mail => "similaruser@somenet.foo")
144 144 u.login = 'NewUser'
145 145 u.password, u.password_confirmation = "password", "password"
146 146 assert !u.save
147 147 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
148 148 end
149 149
150 150 def test_mail_uniqueness_should_not_be_case_sensitive
151 151 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
152 152 u.login = 'newuser1'
153 153 u.password, u.password_confirmation = "password", "password"
154 154 assert u.save
155 155
156 156 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
157 157 u.login = 'newuser2'
158 158 u.password, u.password_confirmation = "password", "password"
159 159 assert !u.save
160 160 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
161 161 end
162 162
163 163 def test_update
164 164 assert_equal "admin", @admin.login
165 165 @admin.login = "john"
166 166 assert @admin.save, @admin.errors.full_messages.join("; ")
167 167 @admin.reload
168 168 assert_equal "john", @admin.login
169 169 end
170 170
171 171 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
172 172 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
173 173 u1.login = 'newuser1'
174 174 assert u1.save
175 175
176 176 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
177 177 u2.login = 'newuser1'
178 178 assert u2.save(:validate => false)
179 179
180 180 user = User.find(u2.id)
181 181 user.firstname = "firstname"
182 182 assert user.save, "Save failed"
183 183 end
184 184
185 185 def test_destroy_should_delete_members_and_roles
186 186 members = Member.where(:user_id => 2)
187 187 ms = members.count
188 188 rs = members.collect(&:roles).flatten.size
189 189 assert ms > 0
190 190 assert rs > 0
191 191 assert_difference 'Member.count', - ms do
192 192 assert_difference 'MemberRole.count', - rs do
193 193 User.find(2).destroy
194 194 end
195 195 end
196 196 assert_nil User.find_by_id(2)
197 197 assert_equal 0, Member.where(:user_id => 2).count
198 198 end
199 199
200 200 def test_destroy_should_update_attachments
201 201 attachment = Attachment.create!(:container => Project.find(1),
202 202 :file => uploaded_test_file("testfile.txt", "text/plain"),
203 203 :author_id => 2)
204 204
205 205 User.find(2).destroy
206 206 assert_nil User.find_by_id(2)
207 207 assert_equal User.anonymous, attachment.reload.author
208 208 end
209 209
210 210 def test_destroy_should_update_comments
211 211 comment = Comment.create!(
212 212 :commented => News.create!(:project_id => 1,
213 213 :author_id => 1, :title => 'foo', :description => 'foo'),
214 214 :author => User.find(2),
215 215 :comments => 'foo'
216 216 )
217 217
218 218 User.find(2).destroy
219 219 assert_nil User.find_by_id(2)
220 220 assert_equal User.anonymous, comment.reload.author
221 221 end
222 222
223 223 def test_destroy_should_update_issues
224 224 issue = Issue.create!(:project_id => 1, :author_id => 2,
225 225 :tracker_id => 1, :subject => 'foo')
226 226
227 227 User.find(2).destroy
228 228 assert_nil User.find_by_id(2)
229 229 assert_equal User.anonymous, issue.reload.author
230 230 end
231 231
232 232 def test_destroy_should_unassign_issues
233 233 issue = Issue.create!(:project_id => 1, :author_id => 1,
234 234 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
235 235
236 236 User.find(2).destroy
237 237 assert_nil User.find_by_id(2)
238 238 assert_nil issue.reload.assigned_to
239 239 end
240 240
241 241 def test_destroy_should_update_journals
242 242 issue = Issue.create!(:project_id => 1, :author_id => 2,
243 243 :tracker_id => 1, :subject => 'foo')
244 244 issue.init_journal(User.find(2), "update")
245 245 issue.save!
246 246
247 247 User.find(2).destroy
248 248 assert_nil User.find_by_id(2)
249 249 assert_equal User.anonymous, issue.journals.first.reload.user
250 250 end
251 251
252 252 def test_destroy_should_update_journal_details_old_value
253 253 issue = Issue.create!(:project_id => 1, :author_id => 1,
254 254 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
255 255 issue.init_journal(User.find(1), "update")
256 256 issue.assigned_to_id = nil
257 257 assert_difference 'JournalDetail.count' do
258 258 issue.save!
259 259 end
260 260 journal_detail = JournalDetail.order('id DESC').first
261 261 assert_equal '2', journal_detail.old_value
262 262
263 263 User.find(2).destroy
264 264 assert_nil User.find_by_id(2)
265 265 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
266 266 end
267 267
268 268 def test_destroy_should_update_journal_details_value
269 269 issue = Issue.create!(:project_id => 1, :author_id => 1,
270 270 :tracker_id => 1, :subject => 'foo')
271 271 issue.init_journal(User.find(1), "update")
272 272 issue.assigned_to_id = 2
273 273 assert_difference 'JournalDetail.count' do
274 274 issue.save!
275 275 end
276 276 journal_detail = JournalDetail.order('id DESC').first
277 277 assert_equal '2', journal_detail.value
278 278
279 279 User.find(2).destroy
280 280 assert_nil User.find_by_id(2)
281 281 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
282 282 end
283 283
284 284 def test_destroy_should_update_messages
285 285 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
286 286 message = Message.create!(:board_id => board.id, :author_id => 2,
287 287 :subject => 'foo', :content => 'foo')
288 288 User.find(2).destroy
289 289 assert_nil User.find_by_id(2)
290 290 assert_equal User.anonymous, message.reload.author
291 291 end
292 292
293 293 def test_destroy_should_update_news
294 294 news = News.create!(:project_id => 1, :author_id => 2,
295 295 :title => 'foo', :description => 'foo')
296 296 User.find(2).destroy
297 297 assert_nil User.find_by_id(2)
298 298 assert_equal User.anonymous, news.reload.author
299 299 end
300 300
301 301 def test_destroy_should_delete_private_queries
302 302 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
303 303 query.project_id = 1
304 304 query.user_id = 2
305 305 query.save!
306 306
307 307 User.find(2).destroy
308 308 assert_nil User.find_by_id(2)
309 309 assert_nil Query.find_by_id(query.id)
310 310 end
311 311
312 312 def test_destroy_should_update_public_queries
313 313 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
314 314 query.project_id = 1
315 315 query.user_id = 2
316 316 query.save!
317 317
318 318 User.find(2).destroy
319 319 assert_nil User.find_by_id(2)
320 320 assert_equal User.anonymous, query.reload.user
321 321 end
322 322
323 323 def test_destroy_should_update_time_entries
324 324 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
325 325 :activity => TimeEntryActivity.create!(:name => 'foo'))
326 326 entry.project_id = 1
327 327 entry.user_id = 2
328 328 entry.save!
329 329
330 330 User.find(2).destroy
331 331 assert_nil User.find_by_id(2)
332 332 assert_equal User.anonymous, entry.reload.user
333 333 end
334 334
335 335 def test_destroy_should_delete_tokens
336 336 token = Token.create!(:user_id => 2, :value => 'foo')
337 337
338 338 User.find(2).destroy
339 339 assert_nil User.find_by_id(2)
340 340 assert_nil Token.find_by_id(token.id)
341 341 end
342 342
343 343 def test_destroy_should_delete_watchers
344 344 issue = Issue.create!(:project_id => 1, :author_id => 1,
345 345 :tracker_id => 1, :subject => 'foo')
346 346 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
347 347
348 348 User.find(2).destroy
349 349 assert_nil User.find_by_id(2)
350 350 assert_nil Watcher.find_by_id(watcher.id)
351 351 end
352 352
353 353 def test_destroy_should_update_wiki_contents
354 354 wiki_content = WikiContent.create!(
355 355 :text => 'foo',
356 356 :author_id => 2,
357 357 :page => WikiPage.create!(:title => 'Foo',
358 358 :wiki => Wiki.create!(:project_id => 3,
359 359 :start_page => 'Start'))
360 360 )
361 361 wiki_content.text = 'bar'
362 362 assert_difference 'WikiContent::Version.count' do
363 363 wiki_content.save!
364 364 end
365 365
366 366 User.find(2).destroy
367 367 assert_nil User.find_by_id(2)
368 368 assert_equal User.anonymous, wiki_content.reload.author
369 369 wiki_content.versions.each do |version|
370 370 assert_equal User.anonymous, version.reload.author
371 371 end
372 372 end
373 373
374 374 def test_destroy_should_nullify_issue_categories
375 375 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
376 376
377 377 User.find(2).destroy
378 378 assert_nil User.find_by_id(2)
379 379 assert_nil category.reload.assigned_to_id
380 380 end
381 381
382 382 def test_destroy_should_nullify_changesets
383 383 changeset = Changeset.create!(
384 384 :repository => Repository::Subversion.create!(
385 385 :project_id => 1,
386 386 :url => 'file:///tmp',
387 387 :identifier => 'tmp'
388 388 ),
389 389 :revision => '12',
390 390 :committed_on => Time.now,
391 391 :committer => 'jsmith'
392 392 )
393 393 assert_equal 2, changeset.user_id
394 394
395 395 User.find(2).destroy
396 396 assert_nil User.find_by_id(2)
397 397 assert_nil changeset.reload.user_id
398 398 end
399 399
400 400 def test_anonymous_user_should_not_be_destroyable
401 401 assert_no_difference 'User.count' do
402 402 assert_equal false, User.anonymous.destroy
403 403 end
404 404 end
405 405
406 406 def test_validate_login_presence
407 407 @admin.login = ""
408 408 assert !@admin.save
409 409 assert_equal 1, @admin.errors.count
410 410 end
411 411
412 412 def test_validate_mail_notification_inclusion
413 413 u = User.new
414 414 u.mail_notification = 'foo'
415 415 u.save
416 416 assert_not_equal [], u.errors[:mail_notification]
417 417 end
418 418
419 419 def test_password
420 420 user = User.try_to_login("admin", "admin")
421 421 assert_kind_of User, user
422 422 assert_equal "admin", user.login
423 423 user.password = "hello123"
424 424 assert user.save
425 425
426 426 user = User.try_to_login("admin", "hello123")
427 427 assert_kind_of User, user
428 428 assert_equal "admin", user.login
429 429 end
430 430
431 431 def test_validate_password_length
432 432 with_settings :password_min_length => '100' do
433 433 user = User.new(:firstname => "new100",
434 434 :lastname => "user100", :mail => "newuser100@somenet.foo")
435 435 user.login = "newuser100"
436 436 user.password, user.password_confirmation = "password100", "password100"
437 437 assert !user.save
438 438 assert_equal 1, user.errors.count
439 439 end
440 440 end
441 441
442 442 def test_name_format
443 443 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
444 444 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
445 445 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
446 446 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
447 447 end
448 448
449 449 def test_name_should_use_setting_as_default_format
450 450 with_settings :user_format => :firstname_lastname do
451 451 assert_equal 'John Smith', @jsmith.reload.name
452 452 end
453 453 with_settings :user_format => :username do
454 454 assert_equal 'jsmith', @jsmith.reload.name
455 455 end
456 456 with_settings :user_format => :lastname do
457 457 assert_equal 'Smith', @jsmith.reload.name
458 458 end
459 459 end
460 460
461 461 def test_today_should_return_the_day_according_to_user_time_zone
462 462 preference = User.find(1).pref
463 463 date = Date.new(2012, 05, 15)
464 464 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
465 465 Date.stubs(:today).returns(date)
466 466 Time.stubs(:now).returns(time)
467 467
468 468 preference.update_attribute :time_zone, 'Baku' # UTC+4
469 469 assert_equal '2012-05-16', User.find(1).today.to_s
470 470
471 471 preference.update_attribute :time_zone, 'La Paz' # UTC-4
472 472 assert_equal '2012-05-15', User.find(1).today.to_s
473 473
474 474 preference.update_attribute :time_zone, ''
475 475 assert_equal '2012-05-15', User.find(1).today.to_s
476 476 end
477 477
478 478 def test_time_to_date_should_return_the_date_according_to_user_time_zone
479 479 preference = User.find(1).pref
480 480 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
481 481
482 482 preference.update_attribute :time_zone, 'Baku' # UTC+4
483 483 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
484 484
485 485 preference.update_attribute :time_zone, 'La Paz' # UTC-4
486 486 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
487 487
488 488 preference.update_attribute :time_zone, ''
489 489 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
490 490 end
491 491
492 492 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
493 493 with_settings :user_format => 'lastname_coma_firstname' do
494 494 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
495 495 User.fields_for_order_statement
496 496 end
497 497 end
498 498
499 499 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
500 500 with_settings :user_format => 'lastname_firstname' do
501 501 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
502 502 User.fields_for_order_statement('authors')
503 503 end
504 504 end
505 505
506 506 def test_fields_for_order_statement_with_blank_format_should_return_default
507 507 with_settings :user_format => '' do
508 508 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
509 509 User.fields_for_order_statement
510 510 end
511 511 end
512 512
513 513 def test_fields_for_order_statement_with_invalid_format_should_return_default
514 514 with_settings :user_format => 'foo' do
515 515 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
516 516 User.fields_for_order_statement
517 517 end
518 518 end
519 519
520 520 test ".try_to_login with good credentials should return the user" do
521 521 user = User.try_to_login("admin", "admin")
522 522 assert_kind_of User, user
523 523 assert_equal "admin", user.login
524 524 end
525 525
526 526 test ".try_to_login with wrong credentials should return nil" do
527 527 assert_nil User.try_to_login("admin", "foo")
528 528 end
529 529
530 530 def test_try_to_login_with_locked_user_should_return_nil
531 531 @jsmith.status = User::STATUS_LOCKED
532 532 @jsmith.save!
533 533
534 534 user = User.try_to_login("jsmith", "jsmith")
535 535 assert_equal nil, user
536 536 end
537 537
538 538 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
539 539 @jsmith.status = User::STATUS_LOCKED
540 540 @jsmith.save!
541 541
542 542 user = User.try_to_login("jsmith", "jsmith", false)
543 543 assert_equal @jsmith, user
544 544 end
545 545
546 546 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
547 547 user = User.try_to_login("AdMin", "admin")
548 548 assert_kind_of User, user
549 549 assert_equal "admin", user.login
550 550 end
551 551
552 552 test ".try_to_login should select the exact matching user first" do
553 553 case_sensitive_user = User.generate! do |user|
554 554 user.password = "admin123"
555 555 end
556 556 # bypass validations to make it appear like existing data
557 557 case_sensitive_user.update_attribute(:login, 'ADMIN')
558 558
559 559 user = User.try_to_login("ADMIN", "admin123")
560 560 assert_kind_of User, user
561 561 assert_equal "ADMIN", user.login
562 562 end
563 563
564 564 if ldap_configured?
565 565 context "#try_to_login using LDAP" do
566 566 context "with failed connection to the LDAP server" do
567 567 should "return nil" do
568 568 @auth_source = AuthSourceLdap.find(1)
569 569 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
570 570
571 571 assert_equal nil, User.try_to_login('edavis', 'wrong')
572 572 end
573 573 end
574 574
575 575 context "with an unsuccessful authentication" do
576 576 should "return nil" do
577 577 assert_equal nil, User.try_to_login('edavis', 'wrong')
578 578 end
579 579 end
580 580
581 581 context "binding with user's account" do
582 582 setup do
583 583 @auth_source = AuthSourceLdap.find(1)
584 584 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
585 585 @auth_source.account_password = ''
586 586 @auth_source.save!
587 587
588 588 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
589 589 @ldap_user.login = 'example1'
590 590 @ldap_user.save!
591 591 end
592 592
593 593 context "with a successful authentication" do
594 594 should "return the user" do
595 595 assert_equal @ldap_user, User.try_to_login('example1', '123456')
596 596 end
597 597 end
598 598
599 599 context "with an unsuccessful authentication" do
600 600 should "return nil" do
601 601 assert_nil User.try_to_login('example1', '11111')
602 602 end
603 603 end
604 604 end
605 605
606 606 context "on the fly registration" do
607 607 setup do
608 608 @auth_source = AuthSourceLdap.find(1)
609 609 @auth_source.update_attribute :onthefly_register, true
610 610 end
611 611
612 612 context "with a successful authentication" do
613 613 should "create a new user account if it doesn't exist" do
614 614 assert_difference('User.count') do
615 615 user = User.try_to_login('edavis', '123456')
616 616 assert !user.admin?
617 617 end
618 618 end
619 619
620 620 should "retrieve existing user" do
621 621 user = User.try_to_login('edavis', '123456')
622 622 user.admin = true
623 623 user.save!
624 624
625 625 assert_no_difference('User.count') do
626 626 user = User.try_to_login('edavis', '123456')
627 627 assert user.admin?
628 628 end
629 629 end
630 630 end
631 631
632 632 context "binding with user's account" do
633 633 setup do
634 634 @auth_source = AuthSourceLdap.find(1)
635 635 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
636 636 @auth_source.account_password = ''
637 637 @auth_source.save!
638 638 end
639 639
640 640 context "with a successful authentication" do
641 641 should "create a new user account if it doesn't exist" do
642 642 assert_difference('User.count') do
643 643 user = User.try_to_login('example1', '123456')
644 644 assert_kind_of User, user
645 645 end
646 646 end
647 647 end
648 648
649 649 context "with an unsuccessful authentication" do
650 650 should "return nil" do
651 651 assert_nil User.try_to_login('example1', '11111')
652 652 end
653 653 end
654 654 end
655 655 end
656 656 end
657 657
658 658 else
659 659 puts "Skipping LDAP tests."
660 660 end
661 661
662 662 def test_create_anonymous
663 663 AnonymousUser.delete_all
664 664 anon = User.anonymous
665 665 assert !anon.new_record?
666 666 assert_kind_of AnonymousUser, anon
667 667 end
668 668
669 669 def test_ensure_single_anonymous_user
670 670 AnonymousUser.delete_all
671 671 anon1 = User.anonymous
672 672 assert !anon1.new_record?
673 673 assert_kind_of AnonymousUser, anon1
674 674 anon2 = AnonymousUser.create(
675 675 :lastname => 'Anonymous', :firstname => '',
676 676 :mail => '', :login => '', :status => 0)
677 677 assert_equal 1, anon2.errors.count
678 678 end
679 679
680 680 def test_rss_key
681 681 assert_nil @jsmith.rss_token
682 682 key = @jsmith.rss_key
683 683 assert_equal 40, key.length
684 684
685 685 @jsmith.reload
686 686 assert_equal key, @jsmith.rss_key
687 687 end
688 688
689 689 def test_rss_key_should_not_be_generated_twice
690 690 assert_difference 'Token.count', 1 do
691 691 key1 = @jsmith.rss_key
692 692 key2 = @jsmith.rss_key
693 693 assert_equal key1, key2
694 694 end
695 695 end
696 696
697 697 def test_api_key_should_not_be_generated_twice
698 698 assert_difference 'Token.count', 1 do
699 699 key1 = @jsmith.api_key
700 700 key2 = @jsmith.api_key
701 701 assert_equal key1, key2
702 702 end
703 703 end
704 704
705 705 test "#api_key should generate a new one if the user doesn't have one" do
706 706 user = User.generate!(:api_token => nil)
707 707 assert_nil user.api_token
708 708
709 709 key = user.api_key
710 710 assert_equal 40, key.length
711 711 user.reload
712 712 assert_equal key, user.api_key
713 713 end
714 714
715 715 test "#api_key should return the existing api token value" do
716 716 user = User.generate!
717 717 token = Token.create!(:action => 'api')
718 718 user.api_token = token
719 719 assert user.save
720 720
721 721 assert_equal token.value, user.api_key
722 722 end
723 723
724 724 test "#find_by_api_key should return nil if no matching key is found" do
725 725 assert_nil User.find_by_api_key('zzzzzzzzz')
726 726 end
727 727
728 728 test "#find_by_api_key should return nil if the key is found for an inactive user" do
729 729 user = User.generate!
730 730 user.status = User::STATUS_LOCKED
731 731 token = Token.create!(:action => 'api')
732 732 user.api_token = token
733 733 user.save
734 734
735 735 assert_nil User.find_by_api_key(token.value)
736 736 end
737 737
738 738 test "#find_by_api_key should return the user if the key is found for an active user" do
739 739 user = User.generate!
740 740 token = Token.create!(:action => 'api')
741 741 user.api_token = token
742 742 user.save
743 743
744 744 assert_equal user, User.find_by_api_key(token.value)
745 745 end
746 746
747 747 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
748 748 user = User.find_by_login("admin")
749 749 user.password = "admin"
750 750 assert user.save(:validate => false)
751 751
752 752 assert_equal false, User.default_admin_account_changed?
753 753 end
754 754
755 755 def test_default_admin_account_changed_should_return_true_if_password_was_changed
756 756 user = User.find_by_login("admin")
757 757 user.password = "newpassword"
758 758 user.save!
759 759
760 760 assert_equal true, User.default_admin_account_changed?
761 761 end
762 762
763 763 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
764 764 user = User.find_by_login("admin")
765 765 user.password = "admin"
766 766 user.status = User::STATUS_LOCKED
767 767 assert user.save(:validate => false)
768 768
769 769 assert_equal true, User.default_admin_account_changed?
770 770 end
771 771
772 772 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
773 773 user = User.find_by_login("admin")
774 774 user.destroy
775 775
776 776 assert_equal true, User.default_admin_account_changed?
777 777 end
778 778
779 779 def test_membership_with_project_should_return_membership
780 780 project = Project.find(1)
781 781
782 782 membership = @jsmith.membership(project)
783 783 assert_kind_of Member, membership
784 784 assert_equal @jsmith, membership.user
785 785 assert_equal project, membership.project
786 786 end
787 787
788 788 def test_membership_with_project_id_should_return_membership
789 789 project = Project.find(1)
790 790
791 791 membership = @jsmith.membership(1)
792 792 assert_kind_of Member, membership
793 793 assert_equal @jsmith, membership.user
794 794 assert_equal project, membership.project
795 795 end
796 796
797 797 def test_membership_for_non_member_should_return_nil
798 798 project = Project.find(1)
799 799
800 800 user = User.generate!
801 801 membership = user.membership(1)
802 802 assert_nil membership
803 803 end
804 804
805 805 def test_roles_for_project
806 806 # user with a role
807 807 roles = @jsmith.roles_for_project(Project.find(1))
808 808 assert_kind_of Role, roles.first
809 809 assert_equal "Manager", roles.first.name
810 810
811 811 # user with no role
812 812 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
813 813 end
814 814
815 815 def test_projects_by_role_for_user_with_role
816 816 user = User.find(2)
817 817 assert_kind_of Hash, user.projects_by_role
818 818 assert_equal 2, user.projects_by_role.size
819 819 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
820 820 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
821 821 end
822 822
823 823 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
824 824 user = User.find(2)
825 825 assert_equal [], user.projects_by_role[Role.find(3)]
826 826 # should not update the hash
827 827 assert_nil user.projects_by_role.values.detect(&:blank?)
828 828 end
829 829
830 830 def test_projects_by_role_for_user_with_no_role
831 831 user = User.generate!
832 832 assert_equal({}, user.projects_by_role)
833 833 end
834 834
835 835 def test_projects_by_role_for_anonymous
836 836 assert_equal({}, User.anonymous.projects_by_role)
837 837 end
838 838
839 839 def test_valid_notification_options
840 840 # without memberships
841 841 assert_equal 5, User.find(7).valid_notification_options.size
842 842 # with memberships
843 843 assert_equal 6, User.find(2).valid_notification_options.size
844 844 end
845 845
846 846 def test_valid_notification_options_class_method
847 847 assert_equal 5, User.valid_notification_options.size
848 848 assert_equal 5, User.valid_notification_options(User.find(7)).size
849 849 assert_equal 6, User.valid_notification_options(User.find(2)).size
850 850 end
851 851
852 852 def test_mail_notification_all
853 853 @jsmith.mail_notification = 'all'
854 854 @jsmith.notified_project_ids = []
855 855 @jsmith.save
856 856 @jsmith.reload
857 857 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
858 858 end
859 859
860 860 def test_mail_notification_selected
861 861 @jsmith.mail_notification = 'selected'
862 862 @jsmith.notified_project_ids = [1]
863 863 @jsmith.save
864 864 @jsmith.reload
865 865 assert Project.find(1).recipients.include?(@jsmith.mail)
866 866 end
867 867
868 868 def test_mail_notification_only_my_events
869 869 @jsmith.mail_notification = 'only_my_events'
870 870 @jsmith.notified_project_ids = []
871 871 @jsmith.save
872 872 @jsmith.reload
873 873 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
874 874 end
875 875
876 876 def test_comments_sorting_preference
877 877 assert !@jsmith.wants_comments_in_reverse_order?
878 878 @jsmith.pref.comments_sorting = 'asc'
879 879 assert !@jsmith.wants_comments_in_reverse_order?
880 880 @jsmith.pref.comments_sorting = 'desc'
881 881 assert @jsmith.wants_comments_in_reverse_order?
882 882 end
883 883
884 884 def test_find_by_mail_should_be_case_insensitive
885 885 u = User.find_by_mail('JSmith@somenet.foo')
886 886 assert_not_nil u
887 887 assert_equal 'jsmith@somenet.foo', u.mail
888 888 end
889 889
890 890 def test_random_password
891 891 u = User.new
892 892 u.random_password
893 893 assert !u.password.blank?
894 894 assert !u.password_confirmation.blank?
895 895 end
896 896
897 897 test "#change_password_allowed? should be allowed if no auth source is set" do
898 898 user = User.generate!
899 899 assert user.change_password_allowed?
900 900 end
901 901
902 902 test "#change_password_allowed? should delegate to the auth source" do
903 903 user = User.generate!
904 904
905 905 allowed_auth_source = AuthSource.generate!
906 906 def allowed_auth_source.allow_password_changes?; true; end
907 907
908 908 denied_auth_source = AuthSource.generate!
909 909 def denied_auth_source.allow_password_changes?; false; end
910 910
911 911 assert user.change_password_allowed?
912 912
913 913 user.auth_source = allowed_auth_source
914 914 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
915 915
916 916 user.auth_source = denied_auth_source
917 917 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
918 918 end
919 919
920 920 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
921 921 with_settings :unsubscribe => '1' do
922 922 assert_equal true, User.find(2).own_account_deletable?
923 923 end
924 924 end
925 925
926 926 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
927 927 with_settings :unsubscribe => '0' do
928 928 assert_equal false, User.find(2).own_account_deletable?
929 929 end
930 930 end
931 931
932 932 def test_own_account_deletable_should_be_false_for_a_single_admin
933 933 User.delete_all(["admin = ? AND id <> ?", true, 1])
934 934
935 935 with_settings :unsubscribe => '1' do
936 936 assert_equal false, User.find(1).own_account_deletable?
937 937 end
938 938 end
939 939
940 940 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
941 941 User.generate! do |user|
942 942 user.admin = true
943 943 end
944 944
945 945 with_settings :unsubscribe => '1' do
946 946 assert_equal true, User.find(1).own_account_deletable?
947 947 end
948 948 end
949 949
950 950 context "#allowed_to?" do
951 951 context "with a unique project" do
952 952 should "return false if project is archived" do
953 953 project = Project.find(1)
954 954 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
955 955 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
956 956 end
957 957
958 958 should "return false for write action if project is closed" do
959 959 project = Project.find(1)
960 960 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
961 961 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
962 962 end
963 963
964 964 should "return true for read action if project is closed" do
965 965 project = Project.find(1)
966 966 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
967 967 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
968 968 end
969 969
970 970 should "return false if related module is disabled" do
971 971 project = Project.find(1)
972 972 project.enabled_module_names = ["issue_tracking"]
973 973 assert_equal true, @admin.allowed_to?(:add_issues, project)
974 974 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
975 975 end
976 976
977 977 should "authorize nearly everything for admin users" do
978 978 project = Project.find(1)
979 979 assert ! @admin.member_of?(project)
980 980 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
981 981 assert_equal true, @admin.allowed_to?(p.to_sym, project)
982 982 end
983 983 end
984 984
985 985 should "authorize normal users depending on their roles" do
986 986 project = Project.find(1)
987 987 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
988 988 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
989 989 end
990 990 end
991 991
992 992 context "with multiple projects" do
993 993 should "return false if array is empty" do
994 994 assert_equal false, @admin.allowed_to?(:view_project, [])
995 995 end
996 996
997 997 should "return true only if user has permission on all these projects" do
998 998 assert_equal true, @admin.allowed_to?(:view_project, Project.all)
999 999 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
1000 1000 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
1001 1001 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
1002 1002 end
1003 1003
1004 1004 should "behave correctly with arrays of 1 project" do
1005 1005 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
1006 1006 end
1007 1007 end
1008 1008
1009 1009 context "with options[:global]" do
1010 1010 should "authorize if user has at least one role that has this permission" do
1011 1011 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1012 1012 @anonymous = User.find(6)
1013 1013 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1014 1014 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1015 1015 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1016 1016 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1017 1017 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1018 1018 end
1019 1019 end
1020 1020 end
1021 1021
1022 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1023 context "#allowed_to_globally?" do
1024 should "proxy to #allowed_to? and reflect global permissions" do
1025 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1026 @anonymous = User.find(6)
1027 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1028 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1029 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1030 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1031 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1032 end
1033 end
1034
1022 1035 context "User#notify_about?" do
1023 1036 context "Issues" do
1024 1037 setup do
1025 1038 @project = Project.find(1)
1026 1039 @author = User.generate!
1027 1040 @assignee = User.generate!
1028 1041 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1029 1042 end
1030 1043
1031 1044 should "be true for a user with :all" do
1032 1045 @author.update_attribute(:mail_notification, 'all')
1033 1046 assert @author.notify_about?(@issue)
1034 1047 end
1035 1048
1036 1049 should "be false for a user with :none" do
1037 1050 @author.update_attribute(:mail_notification, 'none')
1038 1051 assert ! @author.notify_about?(@issue)
1039 1052 end
1040 1053
1041 1054 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
1042 1055 @user = User.generate!(:mail_notification => 'only_my_events')
1043 1056 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1044 1057 assert ! @user.notify_about?(@issue)
1045 1058 end
1046 1059
1047 1060 should "be true for a user with :only_my_events and is the author" do
1048 1061 @author.update_attribute(:mail_notification, 'only_my_events')
1049 1062 assert @author.notify_about?(@issue)
1050 1063 end
1051 1064
1052 1065 should "be true for a user with :only_my_events and is the assignee" do
1053 1066 @assignee.update_attribute(:mail_notification, 'only_my_events')
1054 1067 assert @assignee.notify_about?(@issue)
1055 1068 end
1056 1069
1057 1070 should "be true for a user with :only_assigned and is the assignee" do
1058 1071 @assignee.update_attribute(:mail_notification, 'only_assigned')
1059 1072 assert @assignee.notify_about?(@issue)
1060 1073 end
1061 1074
1062 1075 should "be false for a user with :only_assigned and is not the assignee" do
1063 1076 @author.update_attribute(:mail_notification, 'only_assigned')
1064 1077 assert ! @author.notify_about?(@issue)
1065 1078 end
1066 1079
1067 1080 should "be true for a user with :only_owner and is the author" do
1068 1081 @author.update_attribute(:mail_notification, 'only_owner')
1069 1082 assert @author.notify_about?(@issue)
1070 1083 end
1071 1084
1072 1085 should "be false for a user with :only_owner and is not the author" do
1073 1086 @assignee.update_attribute(:mail_notification, 'only_owner')
1074 1087 assert ! @assignee.notify_about?(@issue)
1075 1088 end
1076 1089
1077 1090 should "be true for a user with :selected and is the author" do
1078 1091 @author.update_attribute(:mail_notification, 'selected')
1079 1092 assert @author.notify_about?(@issue)
1080 1093 end
1081 1094
1082 1095 should "be true for a user with :selected and is the assignee" do
1083 1096 @assignee.update_attribute(:mail_notification, 'selected')
1084 1097 assert @assignee.notify_about?(@issue)
1085 1098 end
1086 1099
1087 1100 should "be false for a user with :selected and is not the author or assignee" do
1088 1101 @user = User.generate!(:mail_notification => 'selected')
1089 1102 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1090 1103 assert ! @user.notify_about?(@issue)
1091 1104 end
1092 1105 end
1093 1106 end
1094 1107
1095 1108 def test_notify_about_news
1096 1109 user = User.generate!
1097 1110 news = News.new
1098 1111
1099 1112 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1100 1113 user.mail_notification = option
1101 1114 assert_equal (option != 'none'), user.notify_about?(news)
1102 1115 end
1103 1116 end
1104 1117
1105 1118 def test_salt_unsalted_passwords
1106 1119 # Restore a user with an unsalted password
1107 1120 user = User.find(1)
1108 1121 user.salt = nil
1109 1122 user.hashed_password = User.hash_password("unsalted")
1110 1123 user.save!
1111 1124
1112 1125 User.salt_unsalted_passwords!
1113 1126
1114 1127 user.reload
1115 1128 # Salt added
1116 1129 assert !user.salt.blank?
1117 1130 # Password still valid
1118 1131 assert user.check_password?("unsalted")
1119 1132 assert_equal user, User.try_to_login(user.login, "unsalted")
1120 1133 end
1121 1134
1122 1135 if Object.const_defined?(:OpenID)
1123 1136 def test_setting_identity_url
1124 1137 normalized_open_id_url = 'http://example.com/'
1125 1138 u = User.new( :identity_url => 'http://example.com/' )
1126 1139 assert_equal normalized_open_id_url, u.identity_url
1127 1140 end
1128 1141
1129 1142 def test_setting_identity_url_without_trailing_slash
1130 1143 normalized_open_id_url = 'http://example.com/'
1131 1144 u = User.new( :identity_url => 'http://example.com' )
1132 1145 assert_equal normalized_open_id_url, u.identity_url
1133 1146 end
1134 1147
1135 1148 def test_setting_identity_url_without_protocol
1136 1149 normalized_open_id_url = 'http://example.com/'
1137 1150 u = User.new( :identity_url => 'example.com' )
1138 1151 assert_equal normalized_open_id_url, u.identity_url
1139 1152 end
1140 1153
1141 1154 def test_setting_blank_identity_url
1142 1155 u = User.new( :identity_url => 'example.com' )
1143 1156 u.identity_url = ''
1144 1157 assert u.identity_url.blank?
1145 1158 end
1146 1159
1147 1160 def test_setting_invalid_identity_url
1148 1161 u = User.new( :identity_url => 'this is not an openid url' )
1149 1162 assert u.identity_url.blank?
1150 1163 end
1151 1164 else
1152 1165 puts "Skipping openid tests."
1153 1166 end
1154 1167 end
General Comments 0
You need to be logged in to leave comments. Login now