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