comparison app/models/user.rb @ 1338:25603efa57b5

Merge from live branch
author Chris Cannam
date Thu, 20 Jun 2013 13:14:14 +0100
parents bb32da3bea34
children 4f746d8966dd 51364c0cd58f
comparison
equal deleted inserted replaced
1209:1b1138f6f55e 1338:25603efa57b5
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
26 STATUS_REGISTERED = 2 26 STATUS_REGISTERED = 2
27 STATUS_LOCKED = 3 27 STATUS_LOCKED = 3
28 28
29 # Different ways of displaying/sorting users 29 # Different ways of displaying/sorting users
30 USER_FORMATS = { 30 USER_FORMATS = {
31 :firstname_lastname => {:string => '#{firstname} #{lastname}', :order => %w(firstname lastname id)}, 31 :firstname_lastname => {
32 :firstname => {:string => '#{firstname}', :order => %w(firstname id)}, 32 :string => '#{firstname} #{lastname}',
33 :lastname_firstname => {:string => '#{lastname} #{firstname}', :order => %w(lastname firstname id)}, 33 :order => %w(firstname lastname id),
34 :lastname_coma_firstname => {:string => '#{lastname}, #{firstname}', :order => %w(lastname firstname id)}, 34 :setting_order => 1
35 :username => {:string => '#{login}', :order => %w(login id)}, 35 },
36 :firstname_lastinitial => {
37 :string => '#{firstname} #{lastname.to_s.chars.first}.',
38 :order => %w(firstname lastname id),
39 :setting_order => 2
40 },
41 :firstname => {
42 :string => '#{firstname}',
43 :order => %w(firstname id),
44 :setting_order => 3
45 },
46 :lastname_firstname => {
47 :string => '#{lastname} #{firstname}',
48 :order => %w(lastname firstname id),
49 :setting_order => 4
50 },
51 :lastname_coma_firstname => {
52 :string => '#{lastname}, #{firstname}',
53 :order => %w(lastname firstname id),
54 :setting_order => 5
55 },
56 :lastname => {
57 :string => '#{lastname}',
58 :order => %w(lastname id),
59 :setting_order => 6
60 },
61 :username => {
62 :string => '#{login}',
63 :order => %w(login id),
64 :setting_order => 7
65 },
36 } 66 }
37 67
38 MAIL_NOTIFICATION_OPTIONS = [ 68 MAIL_NOTIFICATION_OPTIONS = [
39 ['all', :label_user_mail_option_all], 69 ['all', :label_user_mail_option_all],
40 ['selected', :label_user_mail_option_selected], 70 ['selected', :label_user_mail_option_selected],
50 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' 80 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
51 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'" 81 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
52 has_one :api_token, :class_name => 'Token', :conditions => "action='api'" 82 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
53 belongs_to :auth_source 83 belongs_to :auth_source
54 84
85 scope :logged, :conditions => "#{User.table_name}.status <> #{STATUS_ANONYMOUS}"
86 scope :status, lambda {|arg| arg.blank? ? {} : {:conditions => {:status => arg.to_i}} }
87
55 has_one :ssamr_user_detail, :dependent => :destroy, :class_name => 'SsamrUserDetail' 88 has_one :ssamr_user_detail, :dependent => :destroy, :class_name => 'SsamrUserDetail'
56 accepts_nested_attributes_for :ssamr_user_detail 89 accepts_nested_attributes_for :ssamr_user_detail
57 90
58 has_one :author 91 has_one :author
59 92
60 # Active non-anonymous users scope
61 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
62
63 acts_as_customizable 93 acts_as_customizable
64 94
65 attr_accessor :password, :password_confirmation 95 attr_accessor :password, :password_confirmation
66 attr_accessor :last_before_login_on 96 attr_accessor :last_before_login_on
67 # Prevents unauthorized assignments 97 # Prevents unauthorized assignments
68 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password 98 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
69 99
100 LOGIN_LENGTH_LIMIT = 60
101 MAIL_LENGTH_LIMIT = 60
102
70 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } 103 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
104 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
105 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
71 106
72 # TODO: is this validation correct validates_presence_of :ssamr_user_detail
73
74 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
75 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
76 # Login must contain lettres, numbers, underscores only 107 # Login must contain lettres, numbers, underscores only
77 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i 108 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
78 validates_length_of :login, :maximum => 30 109 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
79 validates_length_of :firstname, :lastname, :maximum => 30 110 validates_length_of :firstname, :lastname, :maximum => 30
80 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true 111 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_blank => true
81 validates_length_of :mail, :maximum => 60, :allow_nil => true 112 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
82 validates_confirmation_of :password, :allow_nil => true 113 validates_confirmation_of :password, :allow_nil => true
83 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true 114 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
84 validate :validate_password_length 115 validate :validate_password_length
85 116
86 before_create :set_mail_notification 117 before_create :set_mail_notification
87 before_save :update_hashed_password 118 before_save :update_hashed_password
88 before_destroy :remove_references_before_destroy 119 before_destroy :remove_references_before_destroy
89 120
90 validates_acceptance_of :terms_and_conditions, :on => :create, :message => :must_accept_terms_and_conditions 121 validates_acceptance_of :terms_and_conditions, :on => :create, :message => :must_accept_terms_and_conditions
91 122
92 named_scope :in_group, lambda {|group| 123 scope :in_group, lambda {|group|
93 group_id = group.is_a?(Group) ? group.id : group.to_i 124 group_id = group.is_a?(Group) ? group.id : group.to_i
94 { :conditions => ["#{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] } 125 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)
95 } 126 }
96 named_scope :not_in_group, lambda {|group| 127 scope :not_in_group, lambda {|group|
97 group_id = group.is_a?(Group) ? group.id : group.to_i 128 group_id = group.is_a?(Group) ? group.id : group.to_i
98 { :conditions => ["#{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] } 129 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)
99 } 130 }
100 131
101 def set_mail_notification 132 def set_mail_notification
102 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? 133 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
103 true 134 true
137 self.read_attribute(:identity_url) 168 self.read_attribute(:identity_url)
138 end 169 end
139 170
140 # Returns the user that matches provided login and password, or nil 171 # Returns the user that matches provided login and password, or nil
141 def self.try_to_login(login, password) 172 def self.try_to_login(login, password)
173 login = login.to_s
174 password = password.to_s
175
142 # Make sure no one can sign in with an empty password 176 # Make sure no one can sign in with an empty password
143 return nil if password.to_s.empty? 177 return nil if password.empty?
144 user = find_by_login(login) 178 user = find_by_login(login)
145 if user 179 if user
146 # user is already in local database 180 # user is already in local database
147 return nil if !user.active? 181 return nil if !user.active?
148 if user.auth_source 182 if user.auth_source
171 raise text 205 raise text
172 end 206 end
173 207
174 # Returns the user who matches the given autologin +key+ or nil 208 # Returns the user who matches the given autologin +key+ or nil
175 def self.try_to_autologin(key) 209 def self.try_to_autologin(key)
176 tokens = Token.find_all_by_action_and_value('autologin', key) 210 tokens = Token.find_all_by_action_and_value('autologin', key.to_s)
177 # Make sure there's only 1 token that matches the key 211 # Make sure there's only 1 token that matches the key
178 if tokens.size == 1 212 if tokens.size == 1
179 token = tokens.first 213 token = tokens.first
180 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active? 214 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
181 token.user.update_attribute(:last_login_on, Time.now) 215 token.user.update_attribute(:last_login_on, Time.now)
261 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") 295 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
262 end 296 end
263 297
264 # Does the backend storage allow this user to change their password? 298 # Does the backend storage allow this user to change their password?
265 def change_password_allowed? 299 def change_password_allowed?
266 return true if auth_source_id.blank? 300 return true if auth_source.nil?
267 return auth_source.allow_password_changes? 301 return auth_source.allow_password_changes?
268 end 302 end
269 303
270 # Generate and set a random password. Useful for automated user creation 304 # Generate and set a random password. Useful for automated user creation
271 # Based on Token#generate_token_value 305 # Based on Token#generate_token_value
291 self.pref[:comments_sorting] == 'desc' 325 self.pref[:comments_sorting] == 'desc'
292 end 326 end
293 327
294 # Return user's RSS key (a 40 chars long string), used to access feeds 328 # Return user's RSS key (a 40 chars long string), used to access feeds
295 def rss_key 329 def rss_key
296 token = self.rss_token || Token.create(:user => self, :action => 'feeds') 330 if rss_token.nil?
297 token.value 331 create_rss_token(:action => 'feeds')
332 end
333 rss_token.value
298 end 334 end
299 335
300 # Return user's API key (a 40 chars long string), used to access the API 336 # Return user's API key (a 40 chars long string), used to access the API
301 def api_key 337 def api_key
302 token = self.api_token || self.create_api_token(:action => 'api') 338 if api_token.nil?
303 token.value 339 create_api_token(:action => 'api')
340 end
341 api_token.value
304 end 342 end
305 343
306 # Return an array of project ids for which the user has explicitly turned mail notifications on 344 # Return an array of project ids for which the user has explicitly turned mail notifications on
307 def notified_projects_ids 345 def notified_projects_ids
308 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id) 346 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
331 end 369 end
332 370
333 # Find a user account by matching the exact login and then a case-insensitive 371 # Find a user account by matching the exact login and then a case-insensitive
334 # version. Exact matches will be given priority. 372 # version. Exact matches will be given priority.
335 def self.find_by_login(login) 373 def self.find_by_login(login)
336 # force string comparison to be case sensitive on MySQL
337 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
338
339 # First look for an exact match 374 # First look for an exact match
340 user = first(:conditions => ["#{type_cast} login = ?", login]) 375 user = where(:login => login).all.detect {|u| u.login == login}
341 # Fail over to case-insensitive if none was found 376 unless user
342 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase]) 377 # Fail over to case-insensitive if none was found
378 user = where("LOWER(login) = ?", login.to_s.downcase).first
379 end
380 user
343 end 381 end
344 382
345 def self.find_by_rss_key(key) 383 def self.find_by_rss_key(key)
346 token = Token.find_by_value(key) 384 token = Token.find_by_action_and_value('feeds', key.to_s)
347 token && token.user.active? ? token.user : nil 385 token && token.user.active? ? token.user : nil
348 end 386 end
349 387
350 def self.find_by_api_key(key) 388 def self.find_by_api_key(key)
351 token = Token.find_by_action_and_value('api', key) 389 token = Token.find_by_action_and_value('api', key.to_s)
352 token && token.user.active? ? token.user : nil 390 token && token.user.active? ? token.user : nil
353 end 391 end
354 392
355 # Makes find_by_mail case-insensitive 393 # Makes find_by_mail case-insensitive
356 def self.find_by_mail(mail) 394 def self.find_by_mail(mail)
357 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase]) 395 where("LOWER(mail) = ?", mail.to_s.downcase).first
358 end 396 end
359 397
360 # Returns true if the default admin account can no longer be used 398 # Returns true if the default admin account can no longer be used
361 def self.default_admin_account_changed? 399 def self.default_admin_account_changed?
362 !User.active.find_by_login("admin").try(:check_password?, "admin") 400 !User.active.find_by_login("admin").try(:check_password?, "admin")
363 end 401 end
364 402
365 def to_s 403 def to_s
366 name 404 name
405 end
406
407 CSS_CLASS_BY_STATUS = {
408 STATUS_ANONYMOUS => 'anon',
409 STATUS_ACTIVE => 'active',
410 STATUS_REGISTERED => 'registered',
411 STATUS_LOCKED => 'locked'
412 }
413
414 def css_classes
415 "user #{CSS_CLASS_BY_STATUS[status]}"
367 end 416 end
368 417
369 # Returns the current day according to user's time zone 418 # Returns the current day according to user's time zone
370 def today 419 def today
371 if time_zone.nil? 420 if time_zone.nil?
373 else 422 else
374 Time.now.in_time_zone(time_zone).to_date 423 Time.now.in_time_zone(time_zone).to_date
375 end 424 end
376 end 425 end
377 426
427 # Returns the day of +time+ according to user's time zone
428 def time_to_date(time)
429 if time_zone.nil?
430 time.to_date
431 else
432 time.in_time_zone(time_zone).to_date
433 end
434 end
435
378 def logged? 436 def logged?
379 true 437 true
380 end 438 end
381 439
382 def anonymous? 440 def anonymous?
385 443
386 # Return user's roles for project 444 # Return user's roles for project
387 def roles_for_project(project) 445 def roles_for_project(project)
388 roles = [] 446 roles = []
389 # No role on archived projects 447 # No role on archived projects
390 return roles unless project && project.active? 448 return roles if project.nil? || project.archived?
391 if logged? 449 if logged?
392 # Find project membership 450 # Find project membership
393 membership = memberships.detect {|m| m.project_id == project.id} 451 membership = memberships.detect {|m| m.project_id == project.id}
394 if membership 452 if membership
395 roles = membership.roles 453 roles = membership.roles
411 469
412 # Returns a hash of user's projects grouped by roles 470 # Returns a hash of user's projects grouped by roles
413 def projects_by_role 471 def projects_by_role
414 return @projects_by_role if @projects_by_role 472 return @projects_by_role if @projects_by_role
415 473
416 @projects_by_role = Hash.new {|h,k| h[k]=[]} 474 @projects_by_role = Hash.new([])
417 memberships.each do |membership| 475 memberships.each do |membership|
418 membership.roles.each do |role| 476 if membership.project
419 @projects_by_role[role] << membership.project if membership.project 477 membership.roles.each do |role|
478 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
479 @projects_by_role[role] << membership.project
480 end
420 end 481 end
421 end 482 end
422 @projects_by_role.each do |role, projects| 483 @projects_by_role.each do |role, projects|
423 projects.uniq! 484 projects.uniq!
424 end 485 end
446 # * an array of projects : returns true if user is allowed on every project 507 # * an array of projects : returns true if user is allowed on every project
447 # * nil with options[:global] set : check if user has at least one role allowed for this action, 508 # * nil with options[:global] set : check if user has at least one role allowed for this action,
448 # or falls back to Non Member / Anonymous permissions depending if the user is logged 509 # or falls back to Non Member / Anonymous permissions depending if the user is logged
449 def allowed_to?(action, context, options={}, &block) 510 def allowed_to?(action, context, options={}, &block)
450 if context && context.is_a?(Project) 511 if context && context.is_a?(Project)
451 # No action allowed on archived projects
452 return false unless context.active?
453 # No action allowed on disabled modules
454 return false unless context.allows_to?(action) 512 return false unless context.allows_to?(action)
455 # Admin users are authorized for anything else 513 # Admin users are authorized for anything else
456 return true if admin? 514 return true if admin?
457 515
458 roles = roles_for_project(context) 516 roles = roles_for_project(context)
459 return false unless roles 517 return false unless roles
460 roles.detect {|role| 518 roles.any? {|role|
461 (context.is_public? || role.member?) && 519 (context.is_public? || role.member?) &&
462 role.allowed_to?(action) && 520 role.allowed_to?(action) &&
463 (block_given? ? yield(role, self) : true) 521 (block_given? ? yield(role, self) : true)
464 } 522 }
465 elsif context && context.is_a?(Array) 523 elsif context && context.is_a?(Array)
466 # Authorize if user is authorized on every element of the array 524 if context.empty?
467 context.map do |project| 525 false
468 allowed_to?(action, project, options, &block) 526 else
469 end.inject do |memo,allowed| 527 # Authorize if user is authorized on every element of the array
470 memo && allowed 528 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
471 end 529 end
472 elsif options[:global] 530 elsif options[:global]
473 # Admin users are always authorized 531 # Admin users are always authorized
474 return true if admin? 532 return true if admin?
475 533
476 # authorize if user has at least one role that has this permission 534 # authorize if user has at least one role that has this permission
477 roles = memberships.collect {|m| m.roles}.flatten.uniq 535 roles = memberships.collect {|m| m.roles}.flatten.uniq
478 roles << (self.logged? ? Role.non_member : Role.anonymous) 536 roles << (self.logged? ? Role.non_member : Role.anonymous)
479 roles.detect {|role| 537 roles.any? {|role|
480 role.allowed_to?(action) && 538 role.allowed_to?(action) &&
481 (block_given? ? yield(role, self) : true) 539 (block_given? ? yield(role, self) : true)
482 } 540 }
483 else 541 else
484 false 542 false
487 545
488 # Is the user allowed to do the specified action on any project? 546 # Is the user allowed to do the specified action on any project?
489 # See allowed_to? for the actions and valid options. 547 # See allowed_to? for the actions and valid options.
490 def allowed_to_globally?(action, options, &block) 548 def allowed_to_globally?(action, options, &block)
491 allowed_to?(action, nil, options.reverse_merge(:global => true), &block) 549 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
550 end
551
552 # Returns true if the user is allowed to delete his own account
553 def own_account_deletable?
554 Setting.unsubscribe? &&
555 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
492 end 556 end
493 557
494 safe_attributes 'login', 558 safe_attributes 'login',
495 'firstname', 559 'firstname',
496 'lastname', 560 'lastname',
516 case mail_notification 580 case mail_notification
517 when 'all' 581 when 'all'
518 true 582 true
519 when 'selected' 583 when 'selected'
520 # user receives notifications for created/assigned issues on unselected projects 584 # user receives notifications for created/assigned issues on unselected projects
521 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to)) 585 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was))
522 true 586 true
523 else 587 else
524 false 588 false
525 end 589 end
526 when 'none' 590 when 'none'
527 false 591 false
528 when 'only_my_events' 592 when 'only_my_events'
529 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to)) 593 if object.is_a?(Issue) && (object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was))
530 true 594 true
531 else 595 else
532 false 596 false
533 end 597 end
534 when 'only_assigned' 598 when 'only_assigned'
535 if object.is_a?(Issue) && is_or_belongs_to?(object.assigned_to) 599 if object.is_a?(Issue) && (is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was))
536 true 600 true
537 else 601 else
538 false 602 false
539 end 603 end
540 when 'only_owner' 604 when 'only_owner'
557 end 621 end
558 622
559 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only 623 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
560 # one anonymous user per database. 624 # one anonymous user per database.
561 def self.anonymous 625 def self.anonymous
562 anonymous_user = AnonymousUser.find(:first) 626 anonymous_user = AnonymousUser.first
563 if anonymous_user.nil? 627 if anonymous_user.nil?
564 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0) 628 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
565 raise 'Unable to create the anonymous user.' if anonymous_user.new_record? 629 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
566 end 630 end
567 anonymous_user 631 anonymous_user
570 # Salts all existing unsalted passwords 634 # Salts all existing unsalted passwords
571 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password)) 635 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
572 # This method is used in the SaltPasswords migration and is to be kept as is 636 # This method is used in the SaltPasswords migration and is to be kept as is
573 def self.salt_unsalted_passwords! 637 def self.salt_unsalted_passwords!
574 transaction do 638 transaction do
575 User.find_each(:conditions => "salt IS NULL OR salt = ''") do |user| 639 User.where("salt IS NULL OR salt = ''").find_each do |user|
576 next if user.hashed_password.blank? 640 next if user.hashed_password.blank?
577 salt = User.generate_salt 641 salt = User.generate_salt
578 hashed_password = User.hash_password("#{salt}#{user.hashed_password}") 642 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
579 User.update_all("salt = '#{salt}', hashed_password = '#{hashed_password}'", ["id = ?", user.id] ) 643 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
580 end 644 end
581 end 645 end
582 end 646 end
583 647
584 protected 648 protected
606 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s] 670 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
607 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s] 671 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
608 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] 672 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
609 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] 673 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
610 # Remove private queries and keep public ones 674 # Remove private queries and keep public ones
611 Query.delete_all ['user_id = ? AND is_public = ?', id, false] 675 ::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
612 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] 676 ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
613 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] 677 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
614 Token.delete_all ['user_id = ?', id] 678 Token.delete_all ['user_id = ?', id]
615 Watcher.delete_all ['user_id = ?', id] 679 Watcher.delete_all ['user_id = ?', id]
616 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] 680 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
617 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] 681 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
622 Digest::SHA1.hexdigest(clear_password || "") 686 Digest::SHA1.hexdigest(clear_password || "")
623 end 687 end
624 688
625 # Returns a 128bits random salt as a hex string (32 chars long) 689 # Returns a 128bits random salt as a hex string (32 chars long)
626 def self.generate_salt 690 def self.generate_salt
627 ActiveSupport::SecureRandom.hex(16) 691 Redmine::Utils.random_hex(16)
628 end 692 end
629 693
630 end 694 end
631 695
632 class AnonymousUser < User 696 class AnonymousUser < User
633 697 validate :validate_anonymous_uniqueness, :on => :create
634 def validate_on_create 698
699 def validate_anonymous_uniqueness
635 # There should be only one AnonymousUser in the database 700 # There should be only one AnonymousUser in the database
636 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.find(:first) 701 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.find(:first)
637 end 702 end
638 703
639 def available_custom_fields 704 def available_custom_fields
646 def name(*args); I18n.t(:label_user_anonymous) end 711 def name(*args); I18n.t(:label_user_anonymous) end
647 def mail; nil end 712 def mail; nil end
648 def time_zone; nil end 713 def time_zone; nil end
649 def rss_key; nil end 714 def rss_key; nil end
650 715
716 def pref
717 UserPreference.new(:user => self)
718 end
719
651 # Anonymous user can not be destroyed 720 # Anonymous user can not be destroyed
652 def destroy 721 def destroy
653 false 722 false
654 end 723 end
655 end 724 end