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