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