##// END OF EJS Templates
Rails4: replace deprecated Relation#update_all at User model...
Toshi MARUYAMA -
r12269:ecda4d33e83b
parent child
Show More
@@ -1,745 +1,745
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2013 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 :firstname => {
36 36 :string => '#{firstname}',
37 37 :order => %w(firstname id),
38 38 :setting_order => 3
39 39 },
40 40 :lastname_firstname => {
41 41 :string => '#{lastname} #{firstname}',
42 42 :order => %w(lastname firstname id),
43 43 :setting_order => 4
44 44 },
45 45 :lastname_coma_firstname => {
46 46 :string => '#{lastname}, #{firstname}',
47 47 :order => %w(lastname firstname id),
48 48 :setting_order => 5
49 49 },
50 50 :lastname => {
51 51 :string => '#{lastname}',
52 52 :order => %w(lastname id),
53 53 :setting_order => 6
54 54 },
55 55 :username => {
56 56 :string => '#{login}',
57 57 :order => %w(login id),
58 58 :setting_order => 7
59 59 },
60 60 }
61 61
62 62 MAIL_NOTIFICATION_OPTIONS = [
63 63 ['all', :label_user_mail_option_all],
64 64 ['selected', :label_user_mail_option_selected],
65 65 ['only_my_events', :label_user_mail_option_only_my_events],
66 66 ['only_assigned', :label_user_mail_option_only_assigned],
67 67 ['only_owner', :label_user_mail_option_only_owner],
68 68 ['none', :label_user_mail_option_none]
69 69 ]
70 70
71 71 has_and_belongs_to_many :groups,
72 72 :join_table => "#{table_name_prefix}groups_users#{table_name_suffix}",
73 73 :after_add => Proc.new {|user, group| group.user_added(user)},
74 74 :after_remove => Proc.new {|user, group| group.user_removed(user)}
75 75 has_many :changesets, :dependent => :nullify
76 76 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
77 77 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
78 78 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
79 79 belongs_to :auth_source
80 80
81 81 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
82 82 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
83 83
84 84 acts_as_customizable
85 85
86 86 attr_accessor :password, :password_confirmation, :generate_password
87 87 attr_accessor :last_before_login_on
88 88 # Prevents unauthorized assignments
89 89 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
90 90
91 91 LOGIN_LENGTH_LIMIT = 60
92 92 MAIL_LENGTH_LIMIT = 60
93 93
94 94 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
95 95 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
96 96 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
97 97 # Login must contain letters, numbers, underscores only
98 98 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
99 99 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
100 100 validates_length_of :firstname, :lastname, :maximum => 30
101 101 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
102 102 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
103 103 validates_confirmation_of :password, :allow_nil => true
104 104 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
105 105 validate :validate_password_length
106 106
107 107 before_create :set_mail_notification
108 108 before_save :generate_password_if_needed, :update_hashed_password
109 109 before_destroy :remove_references_before_destroy
110 110 after_save :update_notified_project_ids
111 111
112 112 scope :in_group, lambda {|group|
113 113 group_id = group.is_a?(Group) ? group.id : group.to_i
114 114 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)
115 115 }
116 116 scope :not_in_group, lambda {|group|
117 117 group_id = group.is_a?(Group) ? group.id : group.to_i
118 118 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)
119 119 }
120 120 scope :sorted, lambda { order(*User.fields_for_order_statement)}
121 121
122 122 def set_mail_notification
123 123 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
124 124 true
125 125 end
126 126
127 127 def update_hashed_password
128 128 # update hashed_password if password was set
129 129 if self.password && self.auth_source_id.blank?
130 130 salt_password(password)
131 131 end
132 132 end
133 133
134 134 alias :base_reload :reload
135 135 def reload(*args)
136 136 @name = nil
137 137 @projects_by_role = nil
138 138 @membership_by_project_id = nil
139 139 @notified_projects_ids = nil
140 140 @notified_projects_ids_changed = false
141 141 @builtin_role = nil
142 142 base_reload(*args)
143 143 end
144 144
145 145 def mail=(arg)
146 146 write_attribute(:mail, arg.to_s.strip)
147 147 end
148 148
149 149 def identity_url=(url)
150 150 if url.blank?
151 151 write_attribute(:identity_url, '')
152 152 else
153 153 begin
154 154 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
155 155 rescue OpenIdAuthentication::InvalidOpenId
156 156 # Invalid url, don't save
157 157 end
158 158 end
159 159 self.read_attribute(:identity_url)
160 160 end
161 161
162 162 # Returns the user that matches provided login and password, or nil
163 163 def self.try_to_login(login, password, active_only=true)
164 164 login = login.to_s
165 165 password = password.to_s
166 166
167 167 # Make sure no one can sign in with an empty login or password
168 168 return nil if login.empty? || password.empty?
169 169 user = find_by_login(login)
170 170 if user
171 171 # user is already in local database
172 172 return nil unless user.check_password?(password)
173 173 return nil if !user.active? && active_only
174 174 else
175 175 # user is not yet registered, try to authenticate with available sources
176 176 attrs = AuthSource.authenticate(login, password)
177 177 if attrs
178 178 user = new(attrs)
179 179 user.login = login
180 180 user.language = Setting.default_language
181 181 if user.save
182 182 user.reload
183 183 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
184 184 end
185 185 end
186 186 end
187 187 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
188 188 user
189 189 rescue => text
190 190 raise text
191 191 end
192 192
193 193 # Returns the user who matches the given autologin +key+ or nil
194 194 def self.try_to_autologin(key)
195 195 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
196 196 if user
197 197 user.update_column(:last_login_on, Time.now)
198 198 user
199 199 end
200 200 end
201 201
202 202 def self.name_formatter(formatter = nil)
203 203 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
204 204 end
205 205
206 206 # Returns an array of fields names than can be used to make an order statement for users
207 207 # according to how user names are displayed
208 208 # Examples:
209 209 #
210 210 # User.fields_for_order_statement => ['users.login', 'users.id']
211 211 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
212 212 def self.fields_for_order_statement(table=nil)
213 213 table ||= table_name
214 214 name_formatter[:order].map {|field| "#{table}.#{field}"}
215 215 end
216 216
217 217 # Return user's full name for display
218 218 def name(formatter = nil)
219 219 f = self.class.name_formatter(formatter)
220 220 if formatter
221 221 eval('"' + f[:string] + '"')
222 222 else
223 223 @name ||= eval('"' + f[:string] + '"')
224 224 end
225 225 end
226 226
227 227 def active?
228 228 self.status == STATUS_ACTIVE
229 229 end
230 230
231 231 def registered?
232 232 self.status == STATUS_REGISTERED
233 233 end
234 234
235 235 def locked?
236 236 self.status == STATUS_LOCKED
237 237 end
238 238
239 239 def activate
240 240 self.status = STATUS_ACTIVE
241 241 end
242 242
243 243 def register
244 244 self.status = STATUS_REGISTERED
245 245 end
246 246
247 247 def lock
248 248 self.status = STATUS_LOCKED
249 249 end
250 250
251 251 def activate!
252 252 update_attribute(:status, STATUS_ACTIVE)
253 253 end
254 254
255 255 def register!
256 256 update_attribute(:status, STATUS_REGISTERED)
257 257 end
258 258
259 259 def lock!
260 260 update_attribute(:status, STATUS_LOCKED)
261 261 end
262 262
263 263 # Returns true if +clear_password+ is the correct user's password, otherwise false
264 264 def check_password?(clear_password)
265 265 if auth_source_id.present?
266 266 auth_source.authenticate(self.login, clear_password)
267 267 else
268 268 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
269 269 end
270 270 end
271 271
272 272 # Generates a random salt and computes hashed_password for +clear_password+
273 273 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
274 274 def salt_password(clear_password)
275 275 self.salt = User.generate_salt
276 276 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
277 277 end
278 278
279 279 # Does the backend storage allow this user to change their password?
280 280 def change_password_allowed?
281 281 return true if auth_source.nil?
282 282 return auth_source.allow_password_changes?
283 283 end
284 284
285 285 def must_change_password?
286 286 must_change_passwd? && change_password_allowed?
287 287 end
288 288
289 289 def generate_password?
290 290 generate_password == '1' || generate_password == true
291 291 end
292 292
293 293 # Generate and set a random password on given length
294 294 def random_password(length=40)
295 295 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
296 296 chars -= %w(0 O 1 l)
297 297 password = ''
298 298 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
299 299 self.password = password
300 300 self.password_confirmation = password
301 301 self
302 302 end
303 303
304 304 def pref
305 305 self.preference ||= UserPreference.new(:user => self)
306 306 end
307 307
308 308 def time_zone
309 309 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
310 310 end
311 311
312 312 def wants_comments_in_reverse_order?
313 313 self.pref[:comments_sorting] == 'desc'
314 314 end
315 315
316 316 # Return user's RSS key (a 40 chars long string), used to access feeds
317 317 def rss_key
318 318 if rss_token.nil?
319 319 create_rss_token(:action => 'feeds')
320 320 end
321 321 rss_token.value
322 322 end
323 323
324 324 # Return user's API key (a 40 chars long string), used to access the API
325 325 def api_key
326 326 if api_token.nil?
327 327 create_api_token(:action => 'api')
328 328 end
329 329 api_token.value
330 330 end
331 331
332 332 # Return an array of project ids for which the user has explicitly turned mail notifications on
333 333 def notified_projects_ids
334 334 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
335 335 end
336 336
337 337 def notified_project_ids=(ids)
338 338 @notified_projects_ids_changed = true
339 339 @notified_projects_ids = ids
340 340 end
341 341
342 342 # Updates per project notifications (after_save callback)
343 343 def update_notified_project_ids
344 344 if @notified_projects_ids_changed
345 345 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
346 346 members.update_all(:mail_notification => false)
347 347 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
348 348 end
349 349 end
350 350 private :update_notified_project_ids
351 351
352 352 def valid_notification_options
353 353 self.class.valid_notification_options(self)
354 354 end
355 355
356 356 # Only users that belong to more than 1 project can select projects for which they are notified
357 357 def self.valid_notification_options(user=nil)
358 358 # Note that @user.membership.size would fail since AR ignores
359 359 # :include association option when doing a count
360 360 if user.nil? || user.memberships.length < 1
361 361 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
362 362 else
363 363 MAIL_NOTIFICATION_OPTIONS
364 364 end
365 365 end
366 366
367 367 # Find a user account by matching the exact login and then a case-insensitive
368 368 # version. Exact matches will be given priority.
369 369 def self.find_by_login(login)
370 370 if login.present?
371 371 login = login.to_s
372 372 # First look for an exact match
373 373 user = where(:login => login).all.detect {|u| u.login == login}
374 374 unless user
375 375 # Fail over to case-insensitive if none was found
376 376 user = where("LOWER(login) = ?", login.downcase).first
377 377 end
378 378 user
379 379 end
380 380 end
381 381
382 382 def self.find_by_rss_key(key)
383 383 Token.find_active_user('feeds', key)
384 384 end
385 385
386 386 def self.find_by_api_key(key)
387 387 Token.find_active_user('api', key)
388 388 end
389 389
390 390 # Makes find_by_mail case-insensitive
391 391 def self.find_by_mail(mail)
392 392 where("LOWER(mail) = ?", mail.to_s.downcase).first
393 393 end
394 394
395 395 # Returns true if the default admin account can no longer be used
396 396 def self.default_admin_account_changed?
397 397 !User.active.find_by_login("admin").try(:check_password?, "admin")
398 398 end
399 399
400 400 def to_s
401 401 name
402 402 end
403 403
404 404 CSS_CLASS_BY_STATUS = {
405 405 STATUS_ANONYMOUS => 'anon',
406 406 STATUS_ACTIVE => 'active',
407 407 STATUS_REGISTERED => 'registered',
408 408 STATUS_LOCKED => 'locked'
409 409 }
410 410
411 411 def css_classes
412 412 "user #{CSS_CLASS_BY_STATUS[status]}"
413 413 end
414 414
415 415 # Returns the current day according to user's time zone
416 416 def today
417 417 if time_zone.nil?
418 418 Date.today
419 419 else
420 420 Time.now.in_time_zone(time_zone).to_date
421 421 end
422 422 end
423 423
424 424 # Returns the day of +time+ according to user's time zone
425 425 def time_to_date(time)
426 426 if time_zone.nil?
427 427 time.to_date
428 428 else
429 429 time.in_time_zone(time_zone).to_date
430 430 end
431 431 end
432 432
433 433 def logged?
434 434 true
435 435 end
436 436
437 437 def anonymous?
438 438 !logged?
439 439 end
440 440
441 441 # Returns user's membership for the given project
442 442 # or nil if the user is not a member of project
443 443 def membership(project)
444 444 project_id = project.is_a?(Project) ? project.id : project
445 445
446 446 @membership_by_project_id ||= Hash.new {|h, project_id|
447 447 h[project_id] = memberships.where(:project_id => project_id).first
448 448 }
449 449 @membership_by_project_id[project_id]
450 450 end
451 451
452 452 # Returns the user's bult-in role
453 453 def builtin_role
454 454 @builtin_role ||= Role.non_member
455 455 end
456 456
457 457 # Return user's roles for project
458 458 def roles_for_project(project)
459 459 roles = []
460 460 # No role on archived projects
461 461 return roles if project.nil? || project.archived?
462 462 if membership = membership(project)
463 463 roles = membership.roles
464 464 else
465 465 roles << builtin_role
466 466 end
467 467 roles
468 468 end
469 469
470 470 # Return true if the user is a member of project
471 471 def member_of?(project)
472 472 projects.to_a.include?(project)
473 473 end
474 474
475 475 # Returns a hash of user's projects grouped by roles
476 476 def projects_by_role
477 477 return @projects_by_role if @projects_by_role
478 478
479 479 @projects_by_role = Hash.new([])
480 480 memberships.each do |membership|
481 481 if membership.project
482 482 membership.roles.each do |role|
483 483 @projects_by_role[role] = [] unless @projects_by_role.key?(role)
484 484 @projects_by_role[role] << membership.project
485 485 end
486 486 end
487 487 end
488 488 @projects_by_role.each do |role, projects|
489 489 projects.uniq!
490 490 end
491 491
492 492 @projects_by_role
493 493 end
494 494
495 495 # Returns true if user is arg or belongs to arg
496 496 def is_or_belongs_to?(arg)
497 497 if arg.is_a?(User)
498 498 self == arg
499 499 elsif arg.is_a?(Group)
500 500 arg.users.include?(self)
501 501 else
502 502 false
503 503 end
504 504 end
505 505
506 506 # Return true if the user is allowed to do the specified action on a specific context
507 507 # Action can be:
508 508 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
509 509 # * a permission Symbol (eg. :edit_project)
510 510 # Context can be:
511 511 # * a project : returns true if user is allowed to do the specified action on this project
512 512 # * an array of projects : returns true if user is allowed on every project
513 513 # * nil with options[:global] set : check if user has at least one role allowed for this action,
514 514 # or falls back to Non Member / Anonymous permissions depending if the user is logged
515 515 def allowed_to?(action, context, options={}, &block)
516 516 if context && context.is_a?(Project)
517 517 return false unless context.allows_to?(action)
518 518 # Admin users are authorized for anything else
519 519 return true if admin?
520 520
521 521 roles = roles_for_project(context)
522 522 return false unless roles
523 523 roles.any? {|role|
524 524 (context.is_public? || role.member?) &&
525 525 role.allowed_to?(action) &&
526 526 (block_given? ? yield(role, self) : true)
527 527 }
528 528 elsif context && context.is_a?(Array)
529 529 if context.empty?
530 530 false
531 531 else
532 532 # Authorize if user is authorized on every element of the array
533 533 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
534 534 end
535 535 elsif options[:global]
536 536 # Admin users are always authorized
537 537 return true if admin?
538 538
539 539 # authorize if user has at least one role that has this permission
540 540 roles = memberships.collect {|m| m.roles}.flatten.uniq
541 541 roles << (self.logged? ? Role.non_member : Role.anonymous)
542 542 roles.any? {|role|
543 543 role.allowed_to?(action) &&
544 544 (block_given? ? yield(role, self) : true)
545 545 }
546 546 else
547 547 false
548 548 end
549 549 end
550 550
551 551 # Is the user allowed to do the specified action on any project?
552 552 # See allowed_to? for the actions and valid options.
553 553 def allowed_to_globally?(action, options, &block)
554 554 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
555 555 end
556 556
557 557 # Returns true if the user is allowed to delete the user's own account
558 558 def own_account_deletable?
559 559 Setting.unsubscribe? &&
560 560 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
561 561 end
562 562
563 563 safe_attributes 'login',
564 564 'firstname',
565 565 'lastname',
566 566 'mail',
567 567 'mail_notification',
568 568 'notified_project_ids',
569 569 'language',
570 570 'custom_field_values',
571 571 'custom_fields',
572 572 'identity_url'
573 573
574 574 safe_attributes 'status',
575 575 'auth_source_id',
576 576 'generate_password',
577 577 'must_change_passwd',
578 578 :if => lambda {|user, current_user| current_user.admin?}
579 579
580 580 safe_attributes 'group_ids',
581 581 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
582 582
583 583 # Utility method to help check if a user should be notified about an
584 584 # event.
585 585 #
586 586 # TODO: only supports Issue events currently
587 587 def notify_about?(object)
588 588 if mail_notification == 'all'
589 589 true
590 590 elsif mail_notification.blank? || mail_notification == 'none'
591 591 false
592 592 else
593 593 case object
594 594 when Issue
595 595 case mail_notification
596 596 when 'selected', 'only_my_events'
597 597 # user receives notifications for created/assigned issues on unselected projects
598 598 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
599 599 when 'only_assigned'
600 600 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
601 601 when 'only_owner'
602 602 object.author == self
603 603 end
604 604 when News
605 605 # always send to project members except when mail_notification is set to 'none'
606 606 true
607 607 end
608 608 end
609 609 end
610 610
611 611 def self.current=(user)
612 612 Thread.current[:current_user] = user
613 613 end
614 614
615 615 def self.current
616 616 Thread.current[:current_user] ||= User.anonymous
617 617 end
618 618
619 619 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
620 620 # one anonymous user per database.
621 621 def self.anonymous
622 622 anonymous_user = AnonymousUser.first
623 623 if anonymous_user.nil?
624 624 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
625 625 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
626 626 end
627 627 anonymous_user
628 628 end
629 629
630 630 # Salts all existing unsalted passwords
631 631 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
632 632 # This method is used in the SaltPasswords migration and is to be kept as is
633 633 def self.salt_unsalted_passwords!
634 634 transaction do
635 635 User.where("salt IS NULL OR salt = ''").find_each do |user|
636 636 next if user.hashed_password.blank?
637 637 salt = User.generate_salt
638 638 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
639 639 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
640 640 end
641 641 end
642 642 end
643 643
644 644 protected
645 645
646 646 def validate_password_length
647 647 return if password.blank? && generate_password?
648 648 # Password length validation based on setting
649 649 if !password.nil? && password.size < Setting.password_min_length.to_i
650 650 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
651 651 end
652 652 end
653 653
654 654 private
655 655
656 656 def generate_password_if_needed
657 657 if generate_password? && auth_source.nil?
658 658 length = [Setting.password_min_length.to_i + 2, 10].max
659 659 random_password(length)
660 660 end
661 661 end
662 662
663 663 # Removes references that are not handled by associations
664 664 # Things that are not deleted are reassociated with the anonymous user
665 665 def remove_references_before_destroy
666 666 return if self.id.nil?
667 667
668 668 substitute = User.anonymous
669 669 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
670 670 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
671 671 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
672 672 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
673 673 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
674 674 JournalDetail.
675 675 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
676 676 update_all(['old_value = ?', substitute.id.to_s])
677 677 JournalDetail.
678 678 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
679 679 update_all(['value = ?', substitute.id.to_s])
680 680 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
681 681 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
682 682 # Remove private queries and keep public ones
683 683 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
684 ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
684 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
685 685 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
686 686 Token.delete_all ['user_id = ?', id]
687 687 Watcher.delete_all ['user_id = ?', id]
688 688 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
689 689 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
690 690 end
691 691
692 692 # Return password digest
693 693 def self.hash_password(clear_password)
694 694 Digest::SHA1.hexdigest(clear_password || "")
695 695 end
696 696
697 697 # Returns a 128bits random salt as a hex string (32 chars long)
698 698 def self.generate_salt
699 699 Redmine::Utils.random_hex(16)
700 700 end
701 701
702 702 end
703 703
704 704 class AnonymousUser < User
705 705 validate :validate_anonymous_uniqueness, :on => :create
706 706
707 707 def validate_anonymous_uniqueness
708 708 # There should be only one AnonymousUser in the database
709 709 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
710 710 end
711 711
712 712 def available_custom_fields
713 713 []
714 714 end
715 715
716 716 # Overrides a few properties
717 717 def logged?; false end
718 718 def admin; false end
719 719 def name(*args); I18n.t(:label_user_anonymous) end
720 720 def mail; nil end
721 721 def time_zone; nil end
722 722 def rss_key; nil end
723 723
724 724 def pref
725 725 UserPreference.new(:user => self)
726 726 end
727 727
728 728 # Returns the user's bult-in role
729 729 def builtin_role
730 730 @builtin_role ||= Role.anonymous
731 731 end
732 732
733 733 def membership(*args)
734 734 nil
735 735 end
736 736
737 737 def member_of?(*args)
738 738 false
739 739 end
740 740
741 741 # Anonymous user can not be destroyed
742 742 def destroy
743 743 false
744 744 end
745 745 end
General Comments 0
You need to be logged in to leave comments. Login now