##// END OF EJS Templates
Selected projects in email notifications on "my account" are lost when the page is redisplayed after a validation error (#18060)....
Jean-Philippe Lang -
r13256:827ab38725d7
parent child
Show More
@@ -1,807 +1,807
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 belongs_to :auth_source
84 belongs_to :auth_source
85
85
86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
86 scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
87 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
87 scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
88
88
89 acts_as_customizable
89 acts_as_customizable
90
90
91 attr_accessor :password, :password_confirmation, :generate_password
91 attr_accessor :password, :password_confirmation, :generate_password
92 attr_accessor :last_before_login_on
92 attr_accessor :last_before_login_on
93 # Prevents unauthorized assignments
93 # Prevents unauthorized assignments
94 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
94 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
95
95
96 LOGIN_LENGTH_LIMIT = 60
96 LOGIN_LENGTH_LIMIT = 60
97 MAIL_LENGTH_LIMIT = 60
97 MAIL_LENGTH_LIMIT = 60
98
98
99 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
99 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
100 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
100 validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
101 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
101 validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
102 # Login must contain letters, numbers, underscores only
102 # Login must contain letters, numbers, underscores only
103 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
103 validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
104 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
104 validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
105 validates_length_of :firstname, :lastname, :maximum => 30
105 validates_length_of :firstname, :lastname, :maximum => 30
106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
106 validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
107 validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
108 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
108 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
109 validate :validate_password_length
109 validate :validate_password_length
110 validate do
110 validate do
111 if password_confirmation && password != password_confirmation
111 if password_confirmation && password != password_confirmation
112 errors.add(:password, :confirmation)
112 errors.add(:password, :confirmation)
113 end
113 end
114 end
114 end
115
115
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
130
131 def set_mail_notification
131 def set_mail_notification
132 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
132 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
133 true
133 true
134 end
134 end
135
135
136 def update_hashed_password
136 def update_hashed_password
137 # update hashed_password if password was set
137 # update hashed_password if password was set
138 if self.password && self.auth_source_id.blank?
138 if self.password && self.auth_source_id.blank?
139 salt_password(password)
139 salt_password(password)
140 end
140 end
141 end
141 end
142
142
143 alias :base_reload :reload
143 alias :base_reload :reload
144 def reload(*args)
144 def reload(*args)
145 @name = nil
145 @name = nil
146 @projects_by_role = nil
146 @projects_by_role = nil
147 @membership_by_project_id = nil
147 @membership_by_project_id = nil
148 @notified_projects_ids = nil
148 @notified_projects_ids = nil
149 @notified_projects_ids_changed = false
149 @notified_projects_ids_changed = false
150 @builtin_role = nil
150 @builtin_role = nil
151 @visible_project_ids = nil
151 @visible_project_ids = nil
152 base_reload(*args)
152 base_reload(*args)
153 end
153 end
154
154
155 def mail=(arg)
155 def mail=(arg)
156 write_attribute(:mail, arg.to_s.strip)
156 write_attribute(:mail, arg.to_s.strip)
157 end
157 end
158
158
159 def self.find_or_initialize_by_identity_url(url)
159 def self.find_or_initialize_by_identity_url(url)
160 user = where(:identity_url => url).first
160 user = where(:identity_url => url).first
161 unless user
161 unless user
162 user = User.new
162 user = User.new
163 user.identity_url = url
163 user.identity_url = url
164 end
164 end
165 user
165 user
166 end
166 end
167
167
168 def identity_url=(url)
168 def identity_url=(url)
169 if url.blank?
169 if url.blank?
170 write_attribute(:identity_url, '')
170 write_attribute(:identity_url, '')
171 else
171 else
172 begin
172 begin
173 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
173 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
174 rescue OpenIdAuthentication::InvalidOpenId
174 rescue OpenIdAuthentication::InvalidOpenId
175 # Invalid url, don't save
175 # Invalid url, don't save
176 end
176 end
177 end
177 end
178 self.read_attribute(:identity_url)
178 self.read_attribute(:identity_url)
179 end
179 end
180
180
181 # Returns the user that matches provided login and password, or nil
181 # Returns the user that matches provided login and password, or nil
182 def self.try_to_login(login, password, active_only=true)
182 def self.try_to_login(login, password, active_only=true)
183 login = login.to_s
183 login = login.to_s
184 password = password.to_s
184 password = password.to_s
185
185
186 # Make sure no one can sign in with an empty login or password
186 # Make sure no one can sign in with an empty login or password
187 return nil if login.empty? || password.empty?
187 return nil if login.empty? || password.empty?
188 user = find_by_login(login)
188 user = find_by_login(login)
189 if user
189 if user
190 # user is already in local database
190 # user is already in local database
191 return nil unless user.check_password?(password)
191 return nil unless user.check_password?(password)
192 return nil if !user.active? && active_only
192 return nil if !user.active? && active_only
193 else
193 else
194 # user is not yet registered, try to authenticate with available sources
194 # user is not yet registered, try to authenticate with available sources
195 attrs = AuthSource.authenticate(login, password)
195 attrs = AuthSource.authenticate(login, password)
196 if attrs
196 if attrs
197 user = new(attrs)
197 user = new(attrs)
198 user.login = login
198 user.login = login
199 user.language = Setting.default_language
199 user.language = Setting.default_language
200 if user.save
200 if user.save
201 user.reload
201 user.reload
202 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
202 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
203 end
203 end
204 end
204 end
205 end
205 end
206 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
206 user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active?
207 user
207 user
208 rescue => text
208 rescue => text
209 raise text
209 raise text
210 end
210 end
211
211
212 # Returns the user who matches the given autologin +key+ or nil
212 # Returns the user who matches the given autologin +key+ or nil
213 def self.try_to_autologin(key)
213 def self.try_to_autologin(key)
214 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
214 user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
215 if user
215 if user
216 user.update_column(:last_login_on, Time.now)
216 user.update_column(:last_login_on, Time.now)
217 user
217 user
218 end
218 end
219 end
219 end
220
220
221 def self.name_formatter(formatter = nil)
221 def self.name_formatter(formatter = nil)
222 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
222 USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
223 end
223 end
224
224
225 # Returns an array of fields names than can be used to make an order statement for users
225 # Returns an array of fields names than can be used to make an order statement for users
226 # according to how user names are displayed
226 # according to how user names are displayed
227 # Examples:
227 # Examples:
228 #
228 #
229 # User.fields_for_order_statement => ['users.login', 'users.id']
229 # User.fields_for_order_statement => ['users.login', 'users.id']
230 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
230 # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
231 def self.fields_for_order_statement(table=nil)
231 def self.fields_for_order_statement(table=nil)
232 table ||= table_name
232 table ||= table_name
233 name_formatter[:order].map {|field| "#{table}.#{field}"}
233 name_formatter[:order].map {|field| "#{table}.#{field}"}
234 end
234 end
235
235
236 # Return user's full name for display
236 # Return user's full name for display
237 def name(formatter = nil)
237 def name(formatter = nil)
238 f = self.class.name_formatter(formatter)
238 f = self.class.name_formatter(formatter)
239 if formatter
239 if formatter
240 eval('"' + f[:string] + '"')
240 eval('"' + f[:string] + '"')
241 else
241 else
242 @name ||= eval('"' + f[:string] + '"')
242 @name ||= eval('"' + f[:string] + '"')
243 end
243 end
244 end
244 end
245
245
246 def active?
246 def active?
247 self.status == STATUS_ACTIVE
247 self.status == STATUS_ACTIVE
248 end
248 end
249
249
250 def registered?
250 def registered?
251 self.status == STATUS_REGISTERED
251 self.status == STATUS_REGISTERED
252 end
252 end
253
253
254 def locked?
254 def locked?
255 self.status == STATUS_LOCKED
255 self.status == STATUS_LOCKED
256 end
256 end
257
257
258 def activate
258 def activate
259 self.status = STATUS_ACTIVE
259 self.status = STATUS_ACTIVE
260 end
260 end
261
261
262 def register
262 def register
263 self.status = STATUS_REGISTERED
263 self.status = STATUS_REGISTERED
264 end
264 end
265
265
266 def lock
266 def lock
267 self.status = STATUS_LOCKED
267 self.status = STATUS_LOCKED
268 end
268 end
269
269
270 def activate!
270 def activate!
271 update_attribute(:status, STATUS_ACTIVE)
271 update_attribute(:status, STATUS_ACTIVE)
272 end
272 end
273
273
274 def register!
274 def register!
275 update_attribute(:status, STATUS_REGISTERED)
275 update_attribute(:status, STATUS_REGISTERED)
276 end
276 end
277
277
278 def lock!
278 def lock!
279 update_attribute(:status, STATUS_LOCKED)
279 update_attribute(:status, STATUS_LOCKED)
280 end
280 end
281
281
282 # Returns true if +clear_password+ is the correct user's password, otherwise false
282 # Returns true if +clear_password+ is the correct user's password, otherwise false
283 def check_password?(clear_password)
283 def check_password?(clear_password)
284 if auth_source_id.present?
284 if auth_source_id.present?
285 auth_source.authenticate(self.login, clear_password)
285 auth_source.authenticate(self.login, clear_password)
286 else
286 else
287 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
287 User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
288 end
288 end
289 end
289 end
290
290
291 # Generates a random salt and computes hashed_password for +clear_password+
291 # Generates a random salt and computes hashed_password for +clear_password+
292 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
292 # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
293 def salt_password(clear_password)
293 def salt_password(clear_password)
294 self.salt = User.generate_salt
294 self.salt = User.generate_salt
295 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
295 self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
296 self.passwd_changed_on = Time.now
296 self.passwd_changed_on = Time.now
297 end
297 end
298
298
299 # Does the backend storage allow this user to change their password?
299 # Does the backend storage allow this user to change their password?
300 def change_password_allowed?
300 def change_password_allowed?
301 return true if auth_source.nil?
301 return true if auth_source.nil?
302 return auth_source.allow_password_changes?
302 return auth_source.allow_password_changes?
303 end
303 end
304
304
305 def must_change_password?
305 def must_change_password?
306 must_change_passwd? && change_password_allowed?
306 must_change_passwd? && change_password_allowed?
307 end
307 end
308
308
309 def generate_password?
309 def generate_password?
310 generate_password == '1' || generate_password == true
310 generate_password == '1' || generate_password == true
311 end
311 end
312
312
313 # Generate and set a random password on given length
313 # Generate and set a random password on given length
314 def random_password(length=40)
314 def random_password(length=40)
315 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
315 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
316 chars -= %w(0 O 1 l)
316 chars -= %w(0 O 1 l)
317 password = ''
317 password = ''
318 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
318 length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
319 self.password = password
319 self.password = password
320 self.password_confirmation = password
320 self.password_confirmation = password
321 self
321 self
322 end
322 end
323
323
324 def pref
324 def pref
325 self.preference ||= UserPreference.new(:user => self)
325 self.preference ||= UserPreference.new(:user => self)
326 end
326 end
327
327
328 def time_zone
328 def time_zone
329 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
329 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
330 end
330 end
331
331
332 def force_default_language?
332 def force_default_language?
333 Setting.force_default_language_for_loggedin?
333 Setting.force_default_language_for_loggedin?
334 end
334 end
335
335
336 def language
336 def language
337 if force_default_language?
337 if force_default_language?
338 Setting.default_language
338 Setting.default_language
339 else
339 else
340 super
340 super
341 end
341 end
342 end
342 end
343
343
344 def wants_comments_in_reverse_order?
344 def wants_comments_in_reverse_order?
345 self.pref[:comments_sorting] == 'desc'
345 self.pref[:comments_sorting] == 'desc'
346 end
346 end
347
347
348 # Return user's RSS key (a 40 chars long string), used to access feeds
348 # Return user's RSS key (a 40 chars long string), used to access feeds
349 def rss_key
349 def rss_key
350 if rss_token.nil?
350 if rss_token.nil?
351 create_rss_token(:action => 'feeds')
351 create_rss_token(:action => 'feeds')
352 end
352 end
353 rss_token.value
353 rss_token.value
354 end
354 end
355
355
356 # Return user's API key (a 40 chars long string), used to access the API
356 # Return user's API key (a 40 chars long string), used to access the API
357 def api_key
357 def api_key
358 if api_token.nil?
358 if api_token.nil?
359 create_api_token(:action => 'api')
359 create_api_token(:action => 'api')
360 end
360 end
361 api_token.value
361 api_token.value
362 end
362 end
363
363
364 # Return an array of project ids for which the user has explicitly turned mail notifications on
364 # Return an array of project ids for which the user has explicitly turned mail notifications on
365 def notified_projects_ids
365 def notified_projects_ids
366 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
366 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
367 end
367 end
368
368
369 def notified_project_ids=(ids)
369 def notified_project_ids=(ids)
370 @notified_projects_ids_changed = true
370 @notified_projects_ids_changed = true
371 @notified_projects_ids = ids
371 @notified_projects_ids = ids.map(&:to_i).uniq.select {|n| n > 0}
372 end
372 end
373
373
374 # Updates per project notifications (after_save callback)
374 # Updates per project notifications (after_save callback)
375 def update_notified_project_ids
375 def update_notified_project_ids
376 if @notified_projects_ids_changed
376 if @notified_projects_ids_changed
377 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
377 ids = (mail_notification == 'selected' ? Array.wrap(notified_projects_ids).reject(&:blank?) : [])
378 members.update_all(:mail_notification => false)
378 members.update_all(:mail_notification => false)
379 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
379 members.where(:project_id => ids).update_all(:mail_notification => true) if ids.any?
380 end
380 end
381 end
381 end
382 private :update_notified_project_ids
382 private :update_notified_project_ids
383
383
384 def valid_notification_options
384 def valid_notification_options
385 self.class.valid_notification_options(self)
385 self.class.valid_notification_options(self)
386 end
386 end
387
387
388 # Only users that belong to more than 1 project can select projects for which they are notified
388 # Only users that belong to more than 1 project can select projects for which they are notified
389 def self.valid_notification_options(user=nil)
389 def self.valid_notification_options(user=nil)
390 # Note that @user.membership.size would fail since AR ignores
390 # Note that @user.membership.size would fail since AR ignores
391 # :include association option when doing a count
391 # :include association option when doing a count
392 if user.nil? || user.memberships.length < 1
392 if user.nil? || user.memberships.length < 1
393 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
393 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
394 else
394 else
395 MAIL_NOTIFICATION_OPTIONS
395 MAIL_NOTIFICATION_OPTIONS
396 end
396 end
397 end
397 end
398
398
399 # Find a user account by matching the exact login and then a case-insensitive
399 # Find a user account by matching the exact login and then a case-insensitive
400 # version. Exact matches will be given priority.
400 # version. Exact matches will be given priority.
401 def self.find_by_login(login)
401 def self.find_by_login(login)
402 login = Redmine::CodesetUtil.replace_invalid_utf8(login.to_s)
402 login = Redmine::CodesetUtil.replace_invalid_utf8(login.to_s)
403 if login.present?
403 if login.present?
404 # First look for an exact match
404 # First look for an exact match
405 user = where(:login => login).detect {|u| u.login == login}
405 user = where(:login => login).detect {|u| u.login == login}
406 unless user
406 unless user
407 # Fail over to case-insensitive if none was found
407 # Fail over to case-insensitive if none was found
408 user = where("LOWER(login) = ?", login.downcase).first
408 user = where("LOWER(login) = ?", login.downcase).first
409 end
409 end
410 user
410 user
411 end
411 end
412 end
412 end
413
413
414 def self.find_by_rss_key(key)
414 def self.find_by_rss_key(key)
415 Token.find_active_user('feeds', key)
415 Token.find_active_user('feeds', key)
416 end
416 end
417
417
418 def self.find_by_api_key(key)
418 def self.find_by_api_key(key)
419 Token.find_active_user('api', key)
419 Token.find_active_user('api', key)
420 end
420 end
421
421
422 # Makes find_by_mail case-insensitive
422 # Makes find_by_mail case-insensitive
423 def self.find_by_mail(mail)
423 def self.find_by_mail(mail)
424 where("LOWER(mail) = ?", mail.to_s.downcase).first
424 where("LOWER(mail) = ?", mail.to_s.downcase).first
425 end
425 end
426
426
427 # Returns true if the default admin account can no longer be used
427 # Returns true if the default admin account can no longer be used
428 def self.default_admin_account_changed?
428 def self.default_admin_account_changed?
429 !User.active.find_by_login("admin").try(:check_password?, "admin")
429 !User.active.find_by_login("admin").try(:check_password?, "admin")
430 end
430 end
431
431
432 def to_s
432 def to_s
433 name
433 name
434 end
434 end
435
435
436 CSS_CLASS_BY_STATUS = {
436 CSS_CLASS_BY_STATUS = {
437 STATUS_ANONYMOUS => 'anon',
437 STATUS_ANONYMOUS => 'anon',
438 STATUS_ACTIVE => 'active',
438 STATUS_ACTIVE => 'active',
439 STATUS_REGISTERED => 'registered',
439 STATUS_REGISTERED => 'registered',
440 STATUS_LOCKED => 'locked'
440 STATUS_LOCKED => 'locked'
441 }
441 }
442
442
443 def css_classes
443 def css_classes
444 "user #{CSS_CLASS_BY_STATUS[status]}"
444 "user #{CSS_CLASS_BY_STATUS[status]}"
445 end
445 end
446
446
447 # Returns the current day according to user's time zone
447 # Returns the current day according to user's time zone
448 def today
448 def today
449 if time_zone.nil?
449 if time_zone.nil?
450 Date.today
450 Date.today
451 else
451 else
452 Time.now.in_time_zone(time_zone).to_date
452 Time.now.in_time_zone(time_zone).to_date
453 end
453 end
454 end
454 end
455
455
456 # Returns the day of +time+ according to user's time zone
456 # Returns the day of +time+ according to user's time zone
457 def time_to_date(time)
457 def time_to_date(time)
458 if time_zone.nil?
458 if time_zone.nil?
459 time.to_date
459 time.to_date
460 else
460 else
461 time.in_time_zone(time_zone).to_date
461 time.in_time_zone(time_zone).to_date
462 end
462 end
463 end
463 end
464
464
465 def logged?
465 def logged?
466 true
466 true
467 end
467 end
468
468
469 def anonymous?
469 def anonymous?
470 !logged?
470 !logged?
471 end
471 end
472
472
473 # Returns user's membership for the given project
473 # Returns user's membership for the given project
474 # or nil if the user is not a member of project
474 # or nil if the user is not a member of project
475 def membership(project)
475 def membership(project)
476 project_id = project.is_a?(Project) ? project.id : project
476 project_id = project.is_a?(Project) ? project.id : project
477
477
478 @membership_by_project_id ||= Hash.new {|h, project_id|
478 @membership_by_project_id ||= Hash.new {|h, project_id|
479 h[project_id] = memberships.where(:project_id => project_id).first
479 h[project_id] = memberships.where(:project_id => project_id).first
480 }
480 }
481 @membership_by_project_id[project_id]
481 @membership_by_project_id[project_id]
482 end
482 end
483
483
484 # Returns the user's bult-in role
484 # Returns the user's bult-in role
485 def builtin_role
485 def builtin_role
486 @builtin_role ||= Role.non_member
486 @builtin_role ||= Role.non_member
487 end
487 end
488
488
489 # Return user's roles for project
489 # Return user's roles for project
490 def roles_for_project(project)
490 def roles_for_project(project)
491 # No role on archived projects
491 # No role on archived projects
492 return [] if project.nil? || project.archived?
492 return [] if project.nil? || project.archived?
493 if membership = membership(project)
493 if membership = membership(project)
494 membership.roles.dup
494 membership.roles.dup
495 elsif project.is_public?
495 elsif project.is_public?
496 project.override_roles(builtin_role)
496 project.override_roles(builtin_role)
497 else
497 else
498 []
498 []
499 end
499 end
500 end
500 end
501
501
502 # Returns a hash of user's projects grouped by roles
502 # Returns a hash of user's projects grouped by roles
503 def projects_by_role
503 def projects_by_role
504 return @projects_by_role if @projects_by_role
504 return @projects_by_role if @projects_by_role
505
505
506 hash = Hash.new([])
506 hash = Hash.new([])
507
507
508 group_class = anonymous? ? GroupAnonymous : GroupNonMember
508 group_class = anonymous? ? GroupAnonymous : GroupNonMember
509 members = Member.joins(:project, :principal).
509 members = Member.joins(:project, :principal).
510 where("#{Project.table_name}.status <> 9").
510 where("#{Project.table_name}.status <> 9").
511 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
511 where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Principal.table_name}.type = ?)", self.id, true, group_class.name).
512 preload(:project, :roles).
512 preload(:project, :roles).
513 to_a
513 to_a
514
514
515 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
515 members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)}
516 members.each do |member|
516 members.each do |member|
517 if member.project
517 if member.project
518 member.roles.each do |role|
518 member.roles.each do |role|
519 hash[role] = [] unless hash.key?(role)
519 hash[role] = [] unless hash.key?(role)
520 hash[role] << member.project
520 hash[role] << member.project
521 end
521 end
522 end
522 end
523 end
523 end
524
524
525 hash.each do |role, projects|
525 hash.each do |role, projects|
526 projects.uniq!
526 projects.uniq!
527 end
527 end
528
528
529 @projects_by_role = hash
529 @projects_by_role = hash
530 end
530 end
531
531
532 # Returns the ids of visible projects
532 # Returns the ids of visible projects
533 def visible_project_ids
533 def visible_project_ids
534 @visible_project_ids ||= Project.visible(self).pluck(:id)
534 @visible_project_ids ||= Project.visible(self).pluck(:id)
535 end
535 end
536
536
537 # Returns true if user is arg or belongs to arg
537 # Returns true if user is arg or belongs to arg
538 def is_or_belongs_to?(arg)
538 def is_or_belongs_to?(arg)
539 if arg.is_a?(User)
539 if arg.is_a?(User)
540 self == arg
540 self == arg
541 elsif arg.is_a?(Group)
541 elsif arg.is_a?(Group)
542 arg.users.include?(self)
542 arg.users.include?(self)
543 else
543 else
544 false
544 false
545 end
545 end
546 end
546 end
547
547
548 # Return true if the user is allowed to do the specified action on a specific context
548 # Return true if the user is allowed to do the specified action on a specific context
549 # Action can be:
549 # Action can be:
550 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
550 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
551 # * a permission Symbol (eg. :edit_project)
551 # * a permission Symbol (eg. :edit_project)
552 # Context can be:
552 # Context can be:
553 # * a project : returns true if user is allowed to do the specified action on this project
553 # * a project : returns true if user is allowed to do the specified action on this project
554 # * an array of projects : returns true if user is allowed on every project
554 # * an array of projects : returns true if user is allowed on every project
555 # * nil with options[:global] set : check if user has at least one role allowed for this action,
555 # * nil with options[:global] set : check if user has at least one role allowed for this action,
556 # or falls back to Non Member / Anonymous permissions depending if the user is logged
556 # or falls back to Non Member / Anonymous permissions depending if the user is logged
557 def allowed_to?(action, context, options={}, &block)
557 def allowed_to?(action, context, options={}, &block)
558 if context && context.is_a?(Project)
558 if context && context.is_a?(Project)
559 return false unless context.allows_to?(action)
559 return false unless context.allows_to?(action)
560 # Admin users are authorized for anything else
560 # Admin users are authorized for anything else
561 return true if admin?
561 return true if admin?
562
562
563 roles = roles_for_project(context)
563 roles = roles_for_project(context)
564 return false unless roles
564 return false unless roles
565 roles.any? {|role|
565 roles.any? {|role|
566 (context.is_public? || role.member?) &&
566 (context.is_public? || role.member?) &&
567 role.allowed_to?(action) &&
567 role.allowed_to?(action) &&
568 (block_given? ? yield(role, self) : true)
568 (block_given? ? yield(role, self) : true)
569 }
569 }
570 elsif context && context.is_a?(Array)
570 elsif context && context.is_a?(Array)
571 if context.empty?
571 if context.empty?
572 false
572 false
573 else
573 else
574 # Authorize if user is authorized on every element of the array
574 # Authorize if user is authorized on every element of the array
575 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
575 context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
576 end
576 end
577 elsif context
577 elsif context
578 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
578 raise ArgumentError.new("#allowed_to? context argument must be a Project, an Array of projects or nil")
579 elsif options[:global]
579 elsif options[:global]
580 # Admin users are always authorized
580 # Admin users are always authorized
581 return true if admin?
581 return true if admin?
582
582
583 # authorize if user has at least one role that has this permission
583 # authorize if user has at least one role that has this permission
584 roles = memberships.collect {|m| m.roles}.flatten.uniq
584 roles = memberships.collect {|m| m.roles}.flatten.uniq
585 roles << (self.logged? ? Role.non_member : Role.anonymous)
585 roles << (self.logged? ? Role.non_member : Role.anonymous)
586 roles.any? {|role|
586 roles.any? {|role|
587 role.allowed_to?(action) &&
587 role.allowed_to?(action) &&
588 (block_given? ? yield(role, self) : true)
588 (block_given? ? yield(role, self) : true)
589 }
589 }
590 else
590 else
591 false
591 false
592 end
592 end
593 end
593 end
594
594
595 # Is the user allowed to do the specified action on any project?
595 # Is the user allowed to do the specified action on any project?
596 # See allowed_to? for the actions and valid options.
596 # See allowed_to? for the actions and valid options.
597 #
597 #
598 # NB: this method is not used anywhere in the core codebase as of
598 # NB: this method is not used anywhere in the core codebase as of
599 # 2.5.2, but it's used by many plugins so if we ever want to remove
599 # 2.5.2, but it's used by many plugins so if we ever want to remove
600 # it it has to be carefully deprecated for a version or two.
600 # it it has to be carefully deprecated for a version or two.
601 def allowed_to_globally?(action, options={}, &block)
601 def allowed_to_globally?(action, options={}, &block)
602 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
602 allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
603 end
603 end
604
604
605 # Returns true if the user is allowed to delete the user's own account
605 # Returns true if the user is allowed to delete the user's own account
606 def own_account_deletable?
606 def own_account_deletable?
607 Setting.unsubscribe? &&
607 Setting.unsubscribe? &&
608 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
608 (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
609 end
609 end
610
610
611 safe_attributes 'login',
611 safe_attributes 'login',
612 'firstname',
612 'firstname',
613 'lastname',
613 'lastname',
614 'mail',
614 'mail',
615 'mail_notification',
615 'mail_notification',
616 'notified_project_ids',
616 'notified_project_ids',
617 'language',
617 'language',
618 'custom_field_values',
618 'custom_field_values',
619 'custom_fields',
619 'custom_fields',
620 'identity_url'
620 'identity_url'
621
621
622 safe_attributes 'status',
622 safe_attributes 'status',
623 'auth_source_id',
623 'auth_source_id',
624 'generate_password',
624 'generate_password',
625 'must_change_passwd',
625 'must_change_passwd',
626 :if => lambda {|user, current_user| current_user.admin?}
626 :if => lambda {|user, current_user| current_user.admin?}
627
627
628 safe_attributes 'group_ids',
628 safe_attributes 'group_ids',
629 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
629 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
630
630
631 # Utility method to help check if a user should be notified about an
631 # Utility method to help check if a user should be notified about an
632 # event.
632 # event.
633 #
633 #
634 # TODO: only supports Issue events currently
634 # TODO: only supports Issue events currently
635 def notify_about?(object)
635 def notify_about?(object)
636 if mail_notification == 'all'
636 if mail_notification == 'all'
637 true
637 true
638 elsif mail_notification.blank? || mail_notification == 'none'
638 elsif mail_notification.blank? || mail_notification == 'none'
639 false
639 false
640 else
640 else
641 case object
641 case object
642 when Issue
642 when Issue
643 case mail_notification
643 case mail_notification
644 when 'selected', 'only_my_events'
644 when 'selected', 'only_my_events'
645 # user receives notifications for created/assigned issues on unselected projects
645 # user receives notifications for created/assigned issues on unselected projects
646 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
646 object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
647 when 'only_assigned'
647 when 'only_assigned'
648 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
648 is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
649 when 'only_owner'
649 when 'only_owner'
650 object.author == self
650 object.author == self
651 end
651 end
652 when News
652 when News
653 # always send to project members except when mail_notification is set to 'none'
653 # always send to project members except when mail_notification is set to 'none'
654 true
654 true
655 end
655 end
656 end
656 end
657 end
657 end
658
658
659 def self.current=(user)
659 def self.current=(user)
660 RequestStore.store[:current_user] = user
660 RequestStore.store[:current_user] = user
661 end
661 end
662
662
663 def self.current
663 def self.current
664 RequestStore.store[:current_user] ||= User.anonymous
664 RequestStore.store[:current_user] ||= User.anonymous
665 end
665 end
666
666
667 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
667 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
668 # one anonymous user per database.
668 # one anonymous user per database.
669 def self.anonymous
669 def self.anonymous
670 anonymous_user = AnonymousUser.first
670 anonymous_user = AnonymousUser.first
671 if anonymous_user.nil?
671 if anonymous_user.nil?
672 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
672 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
673 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
673 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
674 end
674 end
675 anonymous_user
675 anonymous_user
676 end
676 end
677
677
678 # Salts all existing unsalted passwords
678 # Salts all existing unsalted passwords
679 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
679 # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
680 # This method is used in the SaltPasswords migration and is to be kept as is
680 # This method is used in the SaltPasswords migration and is to be kept as is
681 def self.salt_unsalted_passwords!
681 def self.salt_unsalted_passwords!
682 transaction do
682 transaction do
683 User.where("salt IS NULL OR salt = ''").find_each do |user|
683 User.where("salt IS NULL OR salt = ''").find_each do |user|
684 next if user.hashed_password.blank?
684 next if user.hashed_password.blank?
685 salt = User.generate_salt
685 salt = User.generate_salt
686 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
686 hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
687 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
687 User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
688 end
688 end
689 end
689 end
690 end
690 end
691
691
692 protected
692 protected
693
693
694 def validate_password_length
694 def validate_password_length
695 return if password.blank? && generate_password?
695 return if password.blank? && generate_password?
696 # Password length validation based on setting
696 # Password length validation based on setting
697 if !password.nil? && password.size < Setting.password_min_length.to_i
697 if !password.nil? && password.size < Setting.password_min_length.to_i
698 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
698 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
699 end
699 end
700 end
700 end
701
701
702 private
702 private
703
703
704 def generate_password_if_needed
704 def generate_password_if_needed
705 if generate_password? && auth_source.nil?
705 if generate_password? && auth_source.nil?
706 length = [Setting.password_min_length.to_i + 2, 10].max
706 length = [Setting.password_min_length.to_i + 2, 10].max
707 random_password(length)
707 random_password(length)
708 end
708 end
709 end
709 end
710
710
711 # Delete all outstanding password reset tokens on password or email change.
711 # Delete all outstanding password reset tokens on password or email change.
712 # Delete the autologin tokens on password change to prohibit session leakage.
712 # Delete the autologin tokens on password change to prohibit session leakage.
713 # This helps to keep the account secure in case the associated email account
713 # This helps to keep the account secure in case the associated email account
714 # was compromised.
714 # was compromised.
715 def destroy_tokens
715 def destroy_tokens
716 tokens = []
716 tokens = []
717 tokens |= ['recovery', 'autologin'] if hashed_password_changed?
717 tokens |= ['recovery', 'autologin'] if hashed_password_changed?
718 tokens |= ['recovery'] if mail_changed?
718 tokens |= ['recovery'] if mail_changed?
719
719
720 if tokens.any?
720 if tokens.any?
721 Token.where(:user_id => id, :action => tokens).delete_all
721 Token.where(:user_id => id, :action => tokens).delete_all
722 end
722 end
723 end
723 end
724
724
725 # Removes references that are not handled by associations
725 # Removes references that are not handled by associations
726 # Things that are not deleted are reassociated with the anonymous user
726 # Things that are not deleted are reassociated with the anonymous user
727 def remove_references_before_destroy
727 def remove_references_before_destroy
728 return if self.id.nil?
728 return if self.id.nil?
729
729
730 substitute = User.anonymous
730 substitute = User.anonymous
731 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
731 Attachment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
732 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
732 Comment.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
733 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
733 Issue.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
734 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
734 Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL')
735 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
735 Journal.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
736 JournalDetail.
736 JournalDetail.
737 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
737 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]).
738 update_all(['old_value = ?', substitute.id.to_s])
738 update_all(['old_value = ?', substitute.id.to_s])
739 JournalDetail.
739 JournalDetail.
740 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
740 where(["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]).
741 update_all(['value = ?', substitute.id.to_s])
741 update_all(['value = ?', substitute.id.to_s])
742 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
742 Message.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
743 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
743 News.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
744 # Remove private queries and keep public ones
744 # Remove private queries and keep public ones
745 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
745 ::Query.delete_all ['user_id = ? AND visibility = ?', id, ::Query::VISIBILITY_PRIVATE]
746 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
746 ::Query.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
747 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
747 TimeEntry.where(['user_id = ?', id]).update_all(['user_id = ?', substitute.id])
748 Token.delete_all ['user_id = ?', id]
748 Token.delete_all ['user_id = ?', id]
749 Watcher.delete_all ['user_id = ?', id]
749 Watcher.delete_all ['user_id = ?', id]
750 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
750 WikiContent.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
751 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
751 WikiContent::Version.where(['author_id = ?', id]).update_all(['author_id = ?', substitute.id])
752 end
752 end
753
753
754 # Return password digest
754 # Return password digest
755 def self.hash_password(clear_password)
755 def self.hash_password(clear_password)
756 Digest::SHA1.hexdigest(clear_password || "")
756 Digest::SHA1.hexdigest(clear_password || "")
757 end
757 end
758
758
759 # Returns a 128bits random salt as a hex string (32 chars long)
759 # Returns a 128bits random salt as a hex string (32 chars long)
760 def self.generate_salt
760 def self.generate_salt
761 Redmine::Utils.random_hex(16)
761 Redmine::Utils.random_hex(16)
762 end
762 end
763
763
764 end
764 end
765
765
766 class AnonymousUser < User
766 class AnonymousUser < User
767 validate :validate_anonymous_uniqueness, :on => :create
767 validate :validate_anonymous_uniqueness, :on => :create
768
768
769 def validate_anonymous_uniqueness
769 def validate_anonymous_uniqueness
770 # There should be only one AnonymousUser in the database
770 # There should be only one AnonymousUser in the database
771 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
771 errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
772 end
772 end
773
773
774 def available_custom_fields
774 def available_custom_fields
775 []
775 []
776 end
776 end
777
777
778 # Overrides a few properties
778 # Overrides a few properties
779 def logged?; false end
779 def logged?; false end
780 def admin; false end
780 def admin; false end
781 def name(*args); I18n.t(:label_user_anonymous) end
781 def name(*args); I18n.t(:label_user_anonymous) end
782 def mail; nil end
782 def mail; nil end
783 def time_zone; nil end
783 def time_zone; nil end
784 def rss_key; nil end
784 def rss_key; nil end
785
785
786 def pref
786 def pref
787 UserPreference.new(:user => self)
787 UserPreference.new(:user => self)
788 end
788 end
789
789
790 # Returns the user's bult-in role
790 # Returns the user's bult-in role
791 def builtin_role
791 def builtin_role
792 @builtin_role ||= Role.anonymous
792 @builtin_role ||= Role.anonymous
793 end
793 end
794
794
795 def membership(*args)
795 def membership(*args)
796 nil
796 nil
797 end
797 end
798
798
799 def member_of?(*args)
799 def member_of?(*args)
800 false
800 false
801 end
801 end
802
802
803 # Anonymous user can not be destroyed
803 # Anonymous user can not be destroyed
804 def destroy
804 def destroy
805 false
805 false
806 end
806 end
807 end
807 end
@@ -1,1266 +1,1271
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 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 File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :projects_trackers,
23 :projects_trackers,
24 :watchers,
24 :watchers,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :journals, :journal_details,
26 :journals, :journal_details,
27 :groups_users,
27 :groups_users,
28 :enabled_modules
28 :enabled_modules
29
29
30 include Redmine::I18n
30 include Redmine::I18n
31
31
32 def setup
32 def setup
33 @admin = User.find(1)
33 @admin = User.find(1)
34 @jsmith = User.find(2)
34 @jsmith = User.find(2)
35 @dlopper = User.find(3)
35 @dlopper = User.find(3)
36 end
36 end
37
37
38 def test_sorted_scope_should_sort_user_by_display_name
38 def test_sorted_scope_should_sort_user_by_display_name
39 # Use .active to ignore anonymous with localized display name
39 # Use .active to ignore anonymous with localized display name
40 assert_equal User.active.map(&:name).map(&:downcase).sort,
40 assert_equal User.active.map(&:name).map(&:downcase).sort,
41 User.active.sorted.map(&:name).map(&:downcase)
41 User.active.sorted.map(&:name).map(&:downcase)
42 end
42 end
43
43
44 def test_generate
44 def test_generate
45 User.generate!(:firstname => 'Testing connection')
45 User.generate!(:firstname => 'Testing connection')
46 User.generate!(:firstname => 'Testing connection')
46 User.generate!(:firstname => 'Testing connection')
47 assert_equal 2, User.where(:firstname => 'Testing connection').count
47 assert_equal 2, User.where(:firstname => 'Testing connection').count
48 end
48 end
49
49
50 def test_truth
50 def test_truth
51 assert_kind_of User, @jsmith
51 assert_kind_of User, @jsmith
52 end
52 end
53
53
54 def test_mail_should_be_stripped
54 def test_mail_should_be_stripped
55 u = User.new
55 u = User.new
56 u.mail = " foo@bar.com "
56 u.mail = " foo@bar.com "
57 assert_equal "foo@bar.com", u.mail
57 assert_equal "foo@bar.com", u.mail
58 end
58 end
59
59
60 def test_mail_validation
60 def test_mail_validation
61 u = User.new
61 u = User.new
62 u.mail = ''
62 u.mail = ''
63 assert !u.valid?
63 assert !u.valid?
64 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
64 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
65 end
65 end
66
66
67 def test_login_length_validation
67 def test_login_length_validation
68 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
68 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
69 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
69 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
70 assert !user.valid?
70 assert !user.valid?
71
71
72 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
72 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
73 assert user.valid?
73 assert user.valid?
74 assert user.save
74 assert user.save
75 end
75 end
76
76
77 def test_generate_password_should_respect_minimum_password_length
77 def test_generate_password_should_respect_minimum_password_length
78 with_settings :password_min_length => 15 do
78 with_settings :password_min_length => 15 do
79 user = User.generate!(:generate_password => true)
79 user = User.generate!(:generate_password => true)
80 assert user.password.length >= 15
80 assert user.password.length >= 15
81 end
81 end
82 end
82 end
83
83
84 def test_generate_password_should_not_generate_password_with_less_than_10_characters
84 def test_generate_password_should_not_generate_password_with_less_than_10_characters
85 with_settings :password_min_length => 4 do
85 with_settings :password_min_length => 4 do
86 user = User.generate!(:generate_password => true)
86 user = User.generate!(:generate_password => true)
87 assert user.password.length >= 10
87 assert user.password.length >= 10
88 end
88 end
89 end
89 end
90
90
91 def test_generate_password_on_create_should_set_password
91 def test_generate_password_on_create_should_set_password
92 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
92 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
93 user.login = "newuser"
93 user.login = "newuser"
94 user.generate_password = true
94 user.generate_password = true
95 assert user.save
95 assert user.save
96
96
97 password = user.password
97 password = user.password
98 assert user.check_password?(password)
98 assert user.check_password?(password)
99 end
99 end
100
100
101 def test_generate_password_on_update_should_update_password
101 def test_generate_password_on_update_should_update_password
102 user = User.find(2)
102 user = User.find(2)
103 hash = user.hashed_password
103 hash = user.hashed_password
104 user.generate_password = true
104 user.generate_password = true
105 assert user.save
105 assert user.save
106
106
107 password = user.password
107 password = user.password
108 assert user.check_password?(password)
108 assert user.check_password?(password)
109 assert_not_equal hash, user.reload.hashed_password
109 assert_not_equal hash, user.reload.hashed_password
110 end
110 end
111
111
112 def test_create
112 def test_create
113 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
113 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
114
114
115 user.login = "jsmith"
115 user.login = "jsmith"
116 user.password, user.password_confirmation = "password", "password"
116 user.password, user.password_confirmation = "password", "password"
117 # login uniqueness
117 # login uniqueness
118 assert !user.save
118 assert !user.save
119 assert_equal 1, user.errors.count
119 assert_equal 1, user.errors.count
120
120
121 user.login = "newuser"
121 user.login = "newuser"
122 user.password, user.password_confirmation = "password", "pass"
122 user.password, user.password_confirmation = "password", "pass"
123 # password confirmation
123 # password confirmation
124 assert !user.save
124 assert !user.save
125 assert_equal 1, user.errors.count
125 assert_equal 1, user.errors.count
126
126
127 user.password, user.password_confirmation = "password", "password"
127 user.password, user.password_confirmation = "password", "password"
128 assert user.save
128 assert user.save
129 end
129 end
130
130
131 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
131 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
132 @user1 = User.generate!
132 @user1 = User.generate!
133 assert_equal 'only_my_events', @user1.mail_notification
133 assert_equal 'only_my_events', @user1.mail_notification
134 with_settings :default_notification_option => 'all' do
134 with_settings :default_notification_option => 'all' do
135 @user2 = User.generate!
135 @user2 = User.generate!
136 assert_equal 'all', @user2.mail_notification
136 assert_equal 'all', @user2.mail_notification
137 end
137 end
138 end
138 end
139
139
140 def test_user_login_should_be_case_insensitive
140 def test_user_login_should_be_case_insensitive
141 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
141 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
142 u.login = 'newuser'
142 u.login = 'newuser'
143 u.password, u.password_confirmation = "password", "password"
143 u.password, u.password_confirmation = "password", "password"
144 assert u.save
144 assert u.save
145 u = User.new(:firstname => "Similar", :lastname => "User",
145 u = User.new(:firstname => "Similar", :lastname => "User",
146 :mail => "similaruser@somenet.foo")
146 :mail => "similaruser@somenet.foo")
147 u.login = 'NewUser'
147 u.login = 'NewUser'
148 u.password, u.password_confirmation = "password", "password"
148 u.password, u.password_confirmation = "password", "password"
149 assert !u.save
149 assert !u.save
150 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
150 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
151 end
151 end
152
152
153 def test_mail_uniqueness_should_not_be_case_sensitive
153 def test_mail_uniqueness_should_not_be_case_sensitive
154 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
154 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
155 u.login = 'newuser1'
155 u.login = 'newuser1'
156 u.password, u.password_confirmation = "password", "password"
156 u.password, u.password_confirmation = "password", "password"
157 assert u.save
157 assert u.save
158
158
159 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
159 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
160 u.login = 'newuser2'
160 u.login = 'newuser2'
161 u.password, u.password_confirmation = "password", "password"
161 u.password, u.password_confirmation = "password", "password"
162 assert !u.save
162 assert !u.save
163 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
163 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
164 end
164 end
165
165
166 def test_update
166 def test_update
167 assert_equal "admin", @admin.login
167 assert_equal "admin", @admin.login
168 @admin.login = "john"
168 @admin.login = "john"
169 assert @admin.save, @admin.errors.full_messages.join("; ")
169 assert @admin.save, @admin.errors.full_messages.join("; ")
170 @admin.reload
170 @admin.reload
171 assert_equal "john", @admin.login
171 assert_equal "john", @admin.login
172 end
172 end
173
173
174 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
174 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
175 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
175 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
176 u1.login = 'newuser1'
176 u1.login = 'newuser1'
177 assert u1.save
177 assert u1.save
178
178
179 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
179 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
180 u2.login = 'newuser1'
180 u2.login = 'newuser1'
181 assert u2.save(:validate => false)
181 assert u2.save(:validate => false)
182
182
183 user = User.find(u2.id)
183 user = User.find(u2.id)
184 user.firstname = "firstname"
184 user.firstname = "firstname"
185 assert user.save, "Save failed"
185 assert user.save, "Save failed"
186 end
186 end
187
187
188 def test_destroy_should_delete_members_and_roles
188 def test_destroy_should_delete_members_and_roles
189 members = Member.where(:user_id => 2)
189 members = Member.where(:user_id => 2)
190 ms = members.count
190 ms = members.count
191 rs = members.collect(&:roles).flatten.size
191 rs = members.collect(&:roles).flatten.size
192 assert ms > 0
192 assert ms > 0
193 assert rs > 0
193 assert rs > 0
194 assert_difference 'Member.count', - ms do
194 assert_difference 'Member.count', - ms do
195 assert_difference 'MemberRole.count', - rs do
195 assert_difference 'MemberRole.count', - rs do
196 User.find(2).destroy
196 User.find(2).destroy
197 end
197 end
198 end
198 end
199 assert_nil User.find_by_id(2)
199 assert_nil User.find_by_id(2)
200 assert_equal 0, Member.where(:user_id => 2).count
200 assert_equal 0, Member.where(:user_id => 2).count
201 end
201 end
202
202
203 def test_destroy_should_update_attachments
203 def test_destroy_should_update_attachments
204 attachment = Attachment.create!(:container => Project.find(1),
204 attachment = Attachment.create!(:container => Project.find(1),
205 :file => uploaded_test_file("testfile.txt", "text/plain"),
205 :file => uploaded_test_file("testfile.txt", "text/plain"),
206 :author_id => 2)
206 :author_id => 2)
207
207
208 User.find(2).destroy
208 User.find(2).destroy
209 assert_nil User.find_by_id(2)
209 assert_nil User.find_by_id(2)
210 assert_equal User.anonymous, attachment.reload.author
210 assert_equal User.anonymous, attachment.reload.author
211 end
211 end
212
212
213 def test_destroy_should_update_comments
213 def test_destroy_should_update_comments
214 comment = Comment.create!(
214 comment = Comment.create!(
215 :commented => News.create!(:project_id => 1,
215 :commented => News.create!(:project_id => 1,
216 :author_id => 1, :title => 'foo', :description => 'foo'),
216 :author_id => 1, :title => 'foo', :description => 'foo'),
217 :author => User.find(2),
217 :author => User.find(2),
218 :comments => 'foo'
218 :comments => 'foo'
219 )
219 )
220
220
221 User.find(2).destroy
221 User.find(2).destroy
222 assert_nil User.find_by_id(2)
222 assert_nil User.find_by_id(2)
223 assert_equal User.anonymous, comment.reload.author
223 assert_equal User.anonymous, comment.reload.author
224 end
224 end
225
225
226 def test_destroy_should_update_issues
226 def test_destroy_should_update_issues
227 issue = Issue.create!(:project_id => 1, :author_id => 2,
227 issue = Issue.create!(:project_id => 1, :author_id => 2,
228 :tracker_id => 1, :subject => 'foo')
228 :tracker_id => 1, :subject => 'foo')
229
229
230 User.find(2).destroy
230 User.find(2).destroy
231 assert_nil User.find_by_id(2)
231 assert_nil User.find_by_id(2)
232 assert_equal User.anonymous, issue.reload.author
232 assert_equal User.anonymous, issue.reload.author
233 end
233 end
234
234
235 def test_destroy_should_unassign_issues
235 def test_destroy_should_unassign_issues
236 issue = Issue.create!(:project_id => 1, :author_id => 1,
236 issue = Issue.create!(:project_id => 1, :author_id => 1,
237 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
237 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
238
238
239 User.find(2).destroy
239 User.find(2).destroy
240 assert_nil User.find_by_id(2)
240 assert_nil User.find_by_id(2)
241 assert_nil issue.reload.assigned_to
241 assert_nil issue.reload.assigned_to
242 end
242 end
243
243
244 def test_destroy_should_update_journals
244 def test_destroy_should_update_journals
245 issue = Issue.create!(:project_id => 1, :author_id => 2,
245 issue = Issue.create!(:project_id => 1, :author_id => 2,
246 :tracker_id => 1, :subject => 'foo')
246 :tracker_id => 1, :subject => 'foo')
247 issue.init_journal(User.find(2), "update")
247 issue.init_journal(User.find(2), "update")
248 issue.save!
248 issue.save!
249
249
250 User.find(2).destroy
250 User.find(2).destroy
251 assert_nil User.find_by_id(2)
251 assert_nil User.find_by_id(2)
252 assert_equal User.anonymous, issue.journals.first.reload.user
252 assert_equal User.anonymous, issue.journals.first.reload.user
253 end
253 end
254
254
255 def test_destroy_should_update_journal_details_old_value
255 def test_destroy_should_update_journal_details_old_value
256 issue = Issue.create!(:project_id => 1, :author_id => 1,
256 issue = Issue.create!(:project_id => 1, :author_id => 1,
257 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
257 :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
258 issue.init_journal(User.find(1), "update")
258 issue.init_journal(User.find(1), "update")
259 issue.assigned_to_id = nil
259 issue.assigned_to_id = nil
260 assert_difference 'JournalDetail.count' do
260 assert_difference 'JournalDetail.count' do
261 issue.save!
261 issue.save!
262 end
262 end
263 journal_detail = JournalDetail.order('id DESC').first
263 journal_detail = JournalDetail.order('id DESC').first
264 assert_equal '2', journal_detail.old_value
264 assert_equal '2', journal_detail.old_value
265
265
266 User.find(2).destroy
266 User.find(2).destroy
267 assert_nil User.find_by_id(2)
267 assert_nil User.find_by_id(2)
268 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
268 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
269 end
269 end
270
270
271 def test_destroy_should_update_journal_details_value
271 def test_destroy_should_update_journal_details_value
272 issue = Issue.create!(:project_id => 1, :author_id => 1,
272 issue = Issue.create!(:project_id => 1, :author_id => 1,
273 :tracker_id => 1, :subject => 'foo')
273 :tracker_id => 1, :subject => 'foo')
274 issue.init_journal(User.find(1), "update")
274 issue.init_journal(User.find(1), "update")
275 issue.assigned_to_id = 2
275 issue.assigned_to_id = 2
276 assert_difference 'JournalDetail.count' do
276 assert_difference 'JournalDetail.count' do
277 issue.save!
277 issue.save!
278 end
278 end
279 journal_detail = JournalDetail.order('id DESC').first
279 journal_detail = JournalDetail.order('id DESC').first
280 assert_equal '2', journal_detail.value
280 assert_equal '2', journal_detail.value
281
281
282 User.find(2).destroy
282 User.find(2).destroy
283 assert_nil User.find_by_id(2)
283 assert_nil User.find_by_id(2)
284 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
284 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
285 end
285 end
286
286
287 def test_destroy_should_update_messages
287 def test_destroy_should_update_messages
288 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
288 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
289 message = Message.create!(:board_id => board.id, :author_id => 2,
289 message = Message.create!(:board_id => board.id, :author_id => 2,
290 :subject => 'foo', :content => 'foo')
290 :subject => 'foo', :content => 'foo')
291 User.find(2).destroy
291 User.find(2).destroy
292 assert_nil User.find_by_id(2)
292 assert_nil User.find_by_id(2)
293 assert_equal User.anonymous, message.reload.author
293 assert_equal User.anonymous, message.reload.author
294 end
294 end
295
295
296 def test_destroy_should_update_news
296 def test_destroy_should_update_news
297 news = News.create!(:project_id => 1, :author_id => 2,
297 news = News.create!(:project_id => 1, :author_id => 2,
298 :title => 'foo', :description => 'foo')
298 :title => 'foo', :description => 'foo')
299 User.find(2).destroy
299 User.find(2).destroy
300 assert_nil User.find_by_id(2)
300 assert_nil User.find_by_id(2)
301 assert_equal User.anonymous, news.reload.author
301 assert_equal User.anonymous, news.reload.author
302 end
302 end
303
303
304 def test_destroy_should_delete_private_queries
304 def test_destroy_should_delete_private_queries
305 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
305 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PRIVATE)
306 query.project_id = 1
306 query.project_id = 1
307 query.user_id = 2
307 query.user_id = 2
308 query.save!
308 query.save!
309
309
310 User.find(2).destroy
310 User.find(2).destroy
311 assert_nil User.find_by_id(2)
311 assert_nil User.find_by_id(2)
312 assert_nil Query.find_by_id(query.id)
312 assert_nil Query.find_by_id(query.id)
313 end
313 end
314
314
315 def test_destroy_should_update_public_queries
315 def test_destroy_should_update_public_queries
316 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
316 query = Query.new(:name => 'foo', :visibility => Query::VISIBILITY_PUBLIC)
317 query.project_id = 1
317 query.project_id = 1
318 query.user_id = 2
318 query.user_id = 2
319 query.save!
319 query.save!
320
320
321 User.find(2).destroy
321 User.find(2).destroy
322 assert_nil User.find_by_id(2)
322 assert_nil User.find_by_id(2)
323 assert_equal User.anonymous, query.reload.user
323 assert_equal User.anonymous, query.reload.user
324 end
324 end
325
325
326 def test_destroy_should_update_time_entries
326 def test_destroy_should_update_time_entries
327 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
327 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today,
328 :activity => TimeEntryActivity.create!(:name => 'foo'))
328 :activity => TimeEntryActivity.create!(:name => 'foo'))
329 entry.project_id = 1
329 entry.project_id = 1
330 entry.user_id = 2
330 entry.user_id = 2
331 entry.save!
331 entry.save!
332
332
333 User.find(2).destroy
333 User.find(2).destroy
334 assert_nil User.find_by_id(2)
334 assert_nil User.find_by_id(2)
335 assert_equal User.anonymous, entry.reload.user
335 assert_equal User.anonymous, entry.reload.user
336 end
336 end
337
337
338 def test_destroy_should_delete_tokens
338 def test_destroy_should_delete_tokens
339 token = Token.create!(:user_id => 2, :value => 'foo')
339 token = Token.create!(:user_id => 2, :value => 'foo')
340
340
341 User.find(2).destroy
341 User.find(2).destroy
342 assert_nil User.find_by_id(2)
342 assert_nil User.find_by_id(2)
343 assert_nil Token.find_by_id(token.id)
343 assert_nil Token.find_by_id(token.id)
344 end
344 end
345
345
346 def test_destroy_should_delete_watchers
346 def test_destroy_should_delete_watchers
347 issue = Issue.create!(:project_id => 1, :author_id => 1,
347 issue = Issue.create!(:project_id => 1, :author_id => 1,
348 :tracker_id => 1, :subject => 'foo')
348 :tracker_id => 1, :subject => 'foo')
349 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
349 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
350
350
351 User.find(2).destroy
351 User.find(2).destroy
352 assert_nil User.find_by_id(2)
352 assert_nil User.find_by_id(2)
353 assert_nil Watcher.find_by_id(watcher.id)
353 assert_nil Watcher.find_by_id(watcher.id)
354 end
354 end
355
355
356 def test_destroy_should_update_wiki_contents
356 def test_destroy_should_update_wiki_contents
357 wiki_content = WikiContent.create!(
357 wiki_content = WikiContent.create!(
358 :text => 'foo',
358 :text => 'foo',
359 :author_id => 2,
359 :author_id => 2,
360 :page => WikiPage.create!(:title => 'Foo',
360 :page => WikiPage.create!(:title => 'Foo',
361 :wiki => Wiki.create!(:project_id => 3,
361 :wiki => Wiki.create!(:project_id => 3,
362 :start_page => 'Start'))
362 :start_page => 'Start'))
363 )
363 )
364 wiki_content.text = 'bar'
364 wiki_content.text = 'bar'
365 assert_difference 'WikiContent::Version.count' do
365 assert_difference 'WikiContent::Version.count' do
366 wiki_content.save!
366 wiki_content.save!
367 end
367 end
368
368
369 User.find(2).destroy
369 User.find(2).destroy
370 assert_nil User.find_by_id(2)
370 assert_nil User.find_by_id(2)
371 assert_equal User.anonymous, wiki_content.reload.author
371 assert_equal User.anonymous, wiki_content.reload.author
372 wiki_content.versions.each do |version|
372 wiki_content.versions.each do |version|
373 assert_equal User.anonymous, version.reload.author
373 assert_equal User.anonymous, version.reload.author
374 end
374 end
375 end
375 end
376
376
377 def test_destroy_should_nullify_issue_categories
377 def test_destroy_should_nullify_issue_categories
378 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
378 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
379
379
380 User.find(2).destroy
380 User.find(2).destroy
381 assert_nil User.find_by_id(2)
381 assert_nil User.find_by_id(2)
382 assert_nil category.reload.assigned_to_id
382 assert_nil category.reload.assigned_to_id
383 end
383 end
384
384
385 def test_destroy_should_nullify_changesets
385 def test_destroy_should_nullify_changesets
386 changeset = Changeset.create!(
386 changeset = Changeset.create!(
387 :repository => Repository::Subversion.create!(
387 :repository => Repository::Subversion.create!(
388 :project_id => 1,
388 :project_id => 1,
389 :url => 'file:///tmp',
389 :url => 'file:///tmp',
390 :identifier => 'tmp'
390 :identifier => 'tmp'
391 ),
391 ),
392 :revision => '12',
392 :revision => '12',
393 :committed_on => Time.now,
393 :committed_on => Time.now,
394 :committer => 'jsmith'
394 :committer => 'jsmith'
395 )
395 )
396 assert_equal 2, changeset.user_id
396 assert_equal 2, changeset.user_id
397
397
398 User.find(2).destroy
398 User.find(2).destroy
399 assert_nil User.find_by_id(2)
399 assert_nil User.find_by_id(2)
400 assert_nil changeset.reload.user_id
400 assert_nil changeset.reload.user_id
401 end
401 end
402
402
403 def test_anonymous_user_should_not_be_destroyable
403 def test_anonymous_user_should_not_be_destroyable
404 assert_no_difference 'User.count' do
404 assert_no_difference 'User.count' do
405 assert_equal false, User.anonymous.destroy
405 assert_equal false, User.anonymous.destroy
406 end
406 end
407 end
407 end
408
408
409 def test_password_change_should_destroy_tokens
409 def test_password_change_should_destroy_tokens
410 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
410 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
411 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
411 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
412
412
413 user = User.find(2)
413 user = User.find(2)
414 user.password, user.password_confirmation = "a new password", "a new password"
414 user.password, user.password_confirmation = "a new password", "a new password"
415 assert user.save
415 assert user.save
416
416
417 assert_nil Token.find_by_id(recovery_token.id)
417 assert_nil Token.find_by_id(recovery_token.id)
418 assert_nil Token.find_by_id(autologin_token.id)
418 assert_nil Token.find_by_id(autologin_token.id)
419 end
419 end
420
420
421 def test_mail_change_should_destroy_tokens
421 def test_mail_change_should_destroy_tokens
422 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
422 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
423 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
423 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
424
424
425 user = User.find(2)
425 user = User.find(2)
426 user.mail = "user@somwehere.com"
426 user.mail = "user@somwehere.com"
427 assert user.save
427 assert user.save
428
428
429 assert_nil Token.find_by_id(recovery_token.id)
429 assert_nil Token.find_by_id(recovery_token.id)
430 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
430 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
431 end
431 end
432
432
433 def test_change_on_other_fields_should_not_destroy_tokens
433 def test_change_on_other_fields_should_not_destroy_tokens
434 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
434 recovery_token = Token.create!(:user_id => 2, :action => 'recovery')
435 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
435 autologin_token = Token.create!(:user_id => 2, :action => 'autologin')
436
436
437 user = User.find(2)
437 user = User.find(2)
438 user.firstname = "Bobby"
438 user.firstname = "Bobby"
439 assert user.save
439 assert user.save
440
440
441 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
441 assert_equal recovery_token, Token.find_by_id(recovery_token.id)
442 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
442 assert_equal autologin_token, Token.find_by_id(autologin_token.id)
443 end
443 end
444
444
445 def test_validate_login_presence
445 def test_validate_login_presence
446 @admin.login = ""
446 @admin.login = ""
447 assert !@admin.save
447 assert !@admin.save
448 assert_equal 1, @admin.errors.count
448 assert_equal 1, @admin.errors.count
449 end
449 end
450
450
451 def test_validate_mail_notification_inclusion
451 def test_validate_mail_notification_inclusion
452 u = User.new
452 u = User.new
453 u.mail_notification = 'foo'
453 u.mail_notification = 'foo'
454 u.save
454 u.save
455 assert_not_equal [], u.errors[:mail_notification]
455 assert_not_equal [], u.errors[:mail_notification]
456 end
456 end
457
457
458 def test_password
458 def test_password
459 user = User.try_to_login("admin", "admin")
459 user = User.try_to_login("admin", "admin")
460 assert_kind_of User, user
460 assert_kind_of User, user
461 assert_equal "admin", user.login
461 assert_equal "admin", user.login
462 user.password = "hello123"
462 user.password = "hello123"
463 assert user.save
463 assert user.save
464
464
465 user = User.try_to_login("admin", "hello123")
465 user = User.try_to_login("admin", "hello123")
466 assert_kind_of User, user
466 assert_kind_of User, user
467 assert_equal "admin", user.login
467 assert_equal "admin", user.login
468 end
468 end
469
469
470 def test_validate_password_length
470 def test_validate_password_length
471 with_settings :password_min_length => '100' do
471 with_settings :password_min_length => '100' do
472 user = User.new(:firstname => "new100",
472 user = User.new(:firstname => "new100",
473 :lastname => "user100", :mail => "newuser100@somenet.foo")
473 :lastname => "user100", :mail => "newuser100@somenet.foo")
474 user.login = "newuser100"
474 user.login = "newuser100"
475 user.password, user.password_confirmation = "password100", "password100"
475 user.password, user.password_confirmation = "password100", "password100"
476 assert !user.save
476 assert !user.save
477 assert_equal 1, user.errors.count
477 assert_equal 1, user.errors.count
478 end
478 end
479 end
479 end
480
480
481 def test_name_format
481 def test_name_format
482 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
482 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
483 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
483 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
484 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
484 assert_equal 'J. Smith', @jsmith.name(:firstinitial_lastname)
485 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
485 assert_equal 'J.-P. Lang', User.new(:firstname => 'Jean-Philippe', :lastname => 'Lang').name(:firstinitial_lastname)
486 end
486 end
487
487
488 def test_name_should_use_setting_as_default_format
488 def test_name_should_use_setting_as_default_format
489 with_settings :user_format => :firstname_lastname do
489 with_settings :user_format => :firstname_lastname do
490 assert_equal 'John Smith', @jsmith.reload.name
490 assert_equal 'John Smith', @jsmith.reload.name
491 end
491 end
492 with_settings :user_format => :username do
492 with_settings :user_format => :username do
493 assert_equal 'jsmith', @jsmith.reload.name
493 assert_equal 'jsmith', @jsmith.reload.name
494 end
494 end
495 with_settings :user_format => :lastname do
495 with_settings :user_format => :lastname do
496 assert_equal 'Smith', @jsmith.reload.name
496 assert_equal 'Smith', @jsmith.reload.name
497 end
497 end
498 end
498 end
499
499
500 def test_today_should_return_the_day_according_to_user_time_zone
500 def test_today_should_return_the_day_according_to_user_time_zone
501 preference = User.find(1).pref
501 preference = User.find(1).pref
502 date = Date.new(2012, 05, 15)
502 date = Date.new(2012, 05, 15)
503 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
503 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
504 Date.stubs(:today).returns(date)
504 Date.stubs(:today).returns(date)
505 Time.stubs(:now).returns(time)
505 Time.stubs(:now).returns(time)
506
506
507 preference.update_attribute :time_zone, 'Baku' # UTC+4
507 preference.update_attribute :time_zone, 'Baku' # UTC+4
508 assert_equal '2012-05-16', User.find(1).today.to_s
508 assert_equal '2012-05-16', User.find(1).today.to_s
509
509
510 preference.update_attribute :time_zone, 'La Paz' # UTC-4
510 preference.update_attribute :time_zone, 'La Paz' # UTC-4
511 assert_equal '2012-05-15', User.find(1).today.to_s
511 assert_equal '2012-05-15', User.find(1).today.to_s
512
512
513 preference.update_attribute :time_zone, ''
513 preference.update_attribute :time_zone, ''
514 assert_equal '2012-05-15', User.find(1).today.to_s
514 assert_equal '2012-05-15', User.find(1).today.to_s
515 end
515 end
516
516
517 def test_time_to_date_should_return_the_date_according_to_user_time_zone
517 def test_time_to_date_should_return_the_date_according_to_user_time_zone
518 preference = User.find(1).pref
518 preference = User.find(1).pref
519 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
519 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
520
520
521 preference.update_attribute :time_zone, 'Baku' # UTC+4
521 preference.update_attribute :time_zone, 'Baku' # UTC+4
522 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
522 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
523
523
524 preference.update_attribute :time_zone, 'La Paz' # UTC-4
524 preference.update_attribute :time_zone, 'La Paz' # UTC-4
525 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
525 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
526
526
527 preference.update_attribute :time_zone, ''
527 preference.update_attribute :time_zone, ''
528 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
528 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
529 end
529 end
530
530
531 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
531 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
532 with_settings :user_format => 'lastname_coma_firstname' do
532 with_settings :user_format => 'lastname_coma_firstname' do
533 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
533 assert_equal ['users.lastname', 'users.firstname', 'users.id'],
534 User.fields_for_order_statement
534 User.fields_for_order_statement
535 end
535 end
536 end
536 end
537
537
538 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
538 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
539 with_settings :user_format => 'lastname_firstname' do
539 with_settings :user_format => 'lastname_firstname' do
540 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
540 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'],
541 User.fields_for_order_statement('authors')
541 User.fields_for_order_statement('authors')
542 end
542 end
543 end
543 end
544
544
545 def test_fields_for_order_statement_with_blank_format_should_return_default
545 def test_fields_for_order_statement_with_blank_format_should_return_default
546 with_settings :user_format => '' do
546 with_settings :user_format => '' do
547 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
547 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
548 User.fields_for_order_statement
548 User.fields_for_order_statement
549 end
549 end
550 end
550 end
551
551
552 def test_fields_for_order_statement_with_invalid_format_should_return_default
552 def test_fields_for_order_statement_with_invalid_format_should_return_default
553 with_settings :user_format => 'foo' do
553 with_settings :user_format => 'foo' do
554 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
554 assert_equal ['users.firstname', 'users.lastname', 'users.id'],
555 User.fields_for_order_statement
555 User.fields_for_order_statement
556 end
556 end
557 end
557 end
558
558
559 test ".try_to_login with good credentials should return the user" do
559 test ".try_to_login with good credentials should return the user" do
560 user = User.try_to_login("admin", "admin")
560 user = User.try_to_login("admin", "admin")
561 assert_kind_of User, user
561 assert_kind_of User, user
562 assert_equal "admin", user.login
562 assert_equal "admin", user.login
563 end
563 end
564
564
565 test ".try_to_login with wrong credentials should return nil" do
565 test ".try_to_login with wrong credentials should return nil" do
566 assert_nil User.try_to_login("admin", "foo")
566 assert_nil User.try_to_login("admin", "foo")
567 end
567 end
568
568
569 def test_try_to_login_with_locked_user_should_return_nil
569 def test_try_to_login_with_locked_user_should_return_nil
570 @jsmith.status = User::STATUS_LOCKED
570 @jsmith.status = User::STATUS_LOCKED
571 @jsmith.save!
571 @jsmith.save!
572
572
573 user = User.try_to_login("jsmith", "jsmith")
573 user = User.try_to_login("jsmith", "jsmith")
574 assert_equal nil, user
574 assert_equal nil, user
575 end
575 end
576
576
577 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
577 def test_try_to_login_with_locked_user_and_not_active_only_should_return_user
578 @jsmith.status = User::STATUS_LOCKED
578 @jsmith.status = User::STATUS_LOCKED
579 @jsmith.save!
579 @jsmith.save!
580
580
581 user = User.try_to_login("jsmith", "jsmith", false)
581 user = User.try_to_login("jsmith", "jsmith", false)
582 assert_equal @jsmith, user
582 assert_equal @jsmith, user
583 end
583 end
584
584
585 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
585 test ".try_to_login should fall-back to case-insensitive if user login is not found as-typed" do
586 user = User.try_to_login("AdMin", "admin")
586 user = User.try_to_login("AdMin", "admin")
587 assert_kind_of User, user
587 assert_kind_of User, user
588 assert_equal "admin", user.login
588 assert_equal "admin", user.login
589 end
589 end
590
590
591 test ".try_to_login should select the exact matching user first" do
591 test ".try_to_login should select the exact matching user first" do
592 case_sensitive_user = User.generate! do |user|
592 case_sensitive_user = User.generate! do |user|
593 user.password = "admin123"
593 user.password = "admin123"
594 end
594 end
595 # bypass validations to make it appear like existing data
595 # bypass validations to make it appear like existing data
596 case_sensitive_user.update_attribute(:login, 'ADMIN')
596 case_sensitive_user.update_attribute(:login, 'ADMIN')
597
597
598 user = User.try_to_login("ADMIN", "admin123")
598 user = User.try_to_login("ADMIN", "admin123")
599 assert_kind_of User, user
599 assert_kind_of User, user
600 assert_equal "ADMIN", user.login
600 assert_equal "ADMIN", user.login
601 end
601 end
602
602
603 if ldap_configured?
603 if ldap_configured?
604 context "#try_to_login using LDAP" do
604 context "#try_to_login using LDAP" do
605 context "with failed connection to the LDAP server" do
605 context "with failed connection to the LDAP server" do
606 should "return nil" do
606 should "return nil" do
607 @auth_source = AuthSourceLdap.find(1)
607 @auth_source = AuthSourceLdap.find(1)
608 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
608 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
609
609
610 assert_equal nil, User.try_to_login('edavis', 'wrong')
610 assert_equal nil, User.try_to_login('edavis', 'wrong')
611 end
611 end
612 end
612 end
613
613
614 context "with an unsuccessful authentication" do
614 context "with an unsuccessful authentication" do
615 should "return nil" do
615 should "return nil" do
616 assert_equal nil, User.try_to_login('edavis', 'wrong')
616 assert_equal nil, User.try_to_login('edavis', 'wrong')
617 end
617 end
618 end
618 end
619
619
620 context "binding with user's account" do
620 context "binding with user's account" do
621 setup do
621 setup do
622 @auth_source = AuthSourceLdap.find(1)
622 @auth_source = AuthSourceLdap.find(1)
623 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
623 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
624 @auth_source.account_password = ''
624 @auth_source.account_password = ''
625 @auth_source.save!
625 @auth_source.save!
626
626
627 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
627 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
628 @ldap_user.login = 'example1'
628 @ldap_user.login = 'example1'
629 @ldap_user.save!
629 @ldap_user.save!
630 end
630 end
631
631
632 context "with a successful authentication" do
632 context "with a successful authentication" do
633 should "return the user" do
633 should "return the user" do
634 assert_equal @ldap_user, User.try_to_login('example1', '123456')
634 assert_equal @ldap_user, User.try_to_login('example1', '123456')
635 end
635 end
636 end
636 end
637
637
638 context "with an unsuccessful authentication" do
638 context "with an unsuccessful authentication" do
639 should "return nil" do
639 should "return nil" do
640 assert_nil User.try_to_login('example1', '11111')
640 assert_nil User.try_to_login('example1', '11111')
641 end
641 end
642 end
642 end
643 end
643 end
644
644
645 context "on the fly registration" do
645 context "on the fly registration" do
646 setup do
646 setup do
647 @auth_source = AuthSourceLdap.find(1)
647 @auth_source = AuthSourceLdap.find(1)
648 @auth_source.update_attribute :onthefly_register, true
648 @auth_source.update_attribute :onthefly_register, true
649 end
649 end
650
650
651 context "with a successful authentication" do
651 context "with a successful authentication" do
652 should "create a new user account if it doesn't exist" do
652 should "create a new user account if it doesn't exist" do
653 assert_difference('User.count') do
653 assert_difference('User.count') do
654 user = User.try_to_login('edavis', '123456')
654 user = User.try_to_login('edavis', '123456')
655 assert !user.admin?
655 assert !user.admin?
656 end
656 end
657 end
657 end
658
658
659 should "retrieve existing user" do
659 should "retrieve existing user" do
660 user = User.try_to_login('edavis', '123456')
660 user = User.try_to_login('edavis', '123456')
661 user.admin = true
661 user.admin = true
662 user.save!
662 user.save!
663
663
664 assert_no_difference('User.count') do
664 assert_no_difference('User.count') do
665 user = User.try_to_login('edavis', '123456')
665 user = User.try_to_login('edavis', '123456')
666 assert user.admin?
666 assert user.admin?
667 end
667 end
668 end
668 end
669 end
669 end
670
670
671 context "binding with user's account" do
671 context "binding with user's account" do
672 setup do
672 setup do
673 @auth_source = AuthSourceLdap.find(1)
673 @auth_source = AuthSourceLdap.find(1)
674 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
674 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
675 @auth_source.account_password = ''
675 @auth_source.account_password = ''
676 @auth_source.save!
676 @auth_source.save!
677 end
677 end
678
678
679 context "with a successful authentication" do
679 context "with a successful authentication" do
680 should "create a new user account if it doesn't exist" do
680 should "create a new user account if it doesn't exist" do
681 assert_difference('User.count') do
681 assert_difference('User.count') do
682 user = User.try_to_login('example1', '123456')
682 user = User.try_to_login('example1', '123456')
683 assert_kind_of User, user
683 assert_kind_of User, user
684 end
684 end
685 end
685 end
686 end
686 end
687
687
688 context "with an unsuccessful authentication" do
688 context "with an unsuccessful authentication" do
689 should "return nil" do
689 should "return nil" do
690 assert_nil User.try_to_login('example1', '11111')
690 assert_nil User.try_to_login('example1', '11111')
691 end
691 end
692 end
692 end
693 end
693 end
694 end
694 end
695 end
695 end
696
696
697 else
697 else
698 puts "Skipping LDAP tests."
698 puts "Skipping LDAP tests."
699 end
699 end
700
700
701 def test_create_anonymous
701 def test_create_anonymous
702 AnonymousUser.delete_all
702 AnonymousUser.delete_all
703 anon = User.anonymous
703 anon = User.anonymous
704 assert !anon.new_record?
704 assert !anon.new_record?
705 assert_kind_of AnonymousUser, anon
705 assert_kind_of AnonymousUser, anon
706 end
706 end
707
707
708 def test_ensure_single_anonymous_user
708 def test_ensure_single_anonymous_user
709 AnonymousUser.delete_all
709 AnonymousUser.delete_all
710 anon1 = User.anonymous
710 anon1 = User.anonymous
711 assert !anon1.new_record?
711 assert !anon1.new_record?
712 assert_kind_of AnonymousUser, anon1
712 assert_kind_of AnonymousUser, anon1
713 anon2 = AnonymousUser.create(
713 anon2 = AnonymousUser.create(
714 :lastname => 'Anonymous', :firstname => '',
714 :lastname => 'Anonymous', :firstname => '',
715 :mail => '', :login => '', :status => 0)
715 :mail => '', :login => '', :status => 0)
716 assert_equal 1, anon2.errors.count
716 assert_equal 1, anon2.errors.count
717 end
717 end
718
718
719 def test_rss_key
719 def test_rss_key
720 assert_nil @jsmith.rss_token
720 assert_nil @jsmith.rss_token
721 key = @jsmith.rss_key
721 key = @jsmith.rss_key
722 assert_equal 40, key.length
722 assert_equal 40, key.length
723
723
724 @jsmith.reload
724 @jsmith.reload
725 assert_equal key, @jsmith.rss_key
725 assert_equal key, @jsmith.rss_key
726 end
726 end
727
727
728 def test_rss_key_should_not_be_generated_twice
728 def test_rss_key_should_not_be_generated_twice
729 assert_difference 'Token.count', 1 do
729 assert_difference 'Token.count', 1 do
730 key1 = @jsmith.rss_key
730 key1 = @jsmith.rss_key
731 key2 = @jsmith.rss_key
731 key2 = @jsmith.rss_key
732 assert_equal key1, key2
732 assert_equal key1, key2
733 end
733 end
734 end
734 end
735
735
736 def test_api_key_should_not_be_generated_twice
736 def test_api_key_should_not_be_generated_twice
737 assert_difference 'Token.count', 1 do
737 assert_difference 'Token.count', 1 do
738 key1 = @jsmith.api_key
738 key1 = @jsmith.api_key
739 key2 = @jsmith.api_key
739 key2 = @jsmith.api_key
740 assert_equal key1, key2
740 assert_equal key1, key2
741 end
741 end
742 end
742 end
743
743
744 test "#api_key should generate a new one if the user doesn't have one" do
744 test "#api_key should generate a new one if the user doesn't have one" do
745 user = User.generate!(:api_token => nil)
745 user = User.generate!(:api_token => nil)
746 assert_nil user.api_token
746 assert_nil user.api_token
747
747
748 key = user.api_key
748 key = user.api_key
749 assert_equal 40, key.length
749 assert_equal 40, key.length
750 user.reload
750 user.reload
751 assert_equal key, user.api_key
751 assert_equal key, user.api_key
752 end
752 end
753
753
754 test "#api_key should return the existing api token value" do
754 test "#api_key should return the existing api token value" do
755 user = User.generate!
755 user = User.generate!
756 token = Token.create!(:action => 'api')
756 token = Token.create!(:action => 'api')
757 user.api_token = token
757 user.api_token = token
758 assert user.save
758 assert user.save
759
759
760 assert_equal token.value, user.api_key
760 assert_equal token.value, user.api_key
761 end
761 end
762
762
763 test "#find_by_api_key should return nil if no matching key is found" do
763 test "#find_by_api_key should return nil if no matching key is found" do
764 assert_nil User.find_by_api_key('zzzzzzzzz')
764 assert_nil User.find_by_api_key('zzzzzzzzz')
765 end
765 end
766
766
767 test "#find_by_api_key should return nil if the key is found for an inactive user" do
767 test "#find_by_api_key should return nil if the key is found for an inactive user" do
768 user = User.generate!
768 user = User.generate!
769 user.status = User::STATUS_LOCKED
769 user.status = User::STATUS_LOCKED
770 token = Token.create!(:action => 'api')
770 token = Token.create!(:action => 'api')
771 user.api_token = token
771 user.api_token = token
772 user.save
772 user.save
773
773
774 assert_nil User.find_by_api_key(token.value)
774 assert_nil User.find_by_api_key(token.value)
775 end
775 end
776
776
777 test "#find_by_api_key should return the user if the key is found for an active user" do
777 test "#find_by_api_key should return the user if the key is found for an active user" do
778 user = User.generate!
778 user = User.generate!
779 token = Token.create!(:action => 'api')
779 token = Token.create!(:action => 'api')
780 user.api_token = token
780 user.api_token = token
781 user.save
781 user.save
782
782
783 assert_equal user, User.find_by_api_key(token.value)
783 assert_equal user, User.find_by_api_key(token.value)
784 end
784 end
785
785
786 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
786 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
787 user = User.find_by_login("admin")
787 user = User.find_by_login("admin")
788 user.password = "admin"
788 user.password = "admin"
789 assert user.save(:validate => false)
789 assert user.save(:validate => false)
790
790
791 assert_equal false, User.default_admin_account_changed?
791 assert_equal false, User.default_admin_account_changed?
792 end
792 end
793
793
794 def test_default_admin_account_changed_should_return_true_if_password_was_changed
794 def test_default_admin_account_changed_should_return_true_if_password_was_changed
795 user = User.find_by_login("admin")
795 user = User.find_by_login("admin")
796 user.password = "newpassword"
796 user.password = "newpassword"
797 user.save!
797 user.save!
798
798
799 assert_equal true, User.default_admin_account_changed?
799 assert_equal true, User.default_admin_account_changed?
800 end
800 end
801
801
802 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
802 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
803 user = User.find_by_login("admin")
803 user = User.find_by_login("admin")
804 user.password = "admin"
804 user.password = "admin"
805 user.status = User::STATUS_LOCKED
805 user.status = User::STATUS_LOCKED
806 assert user.save(:validate => false)
806 assert user.save(:validate => false)
807
807
808 assert_equal true, User.default_admin_account_changed?
808 assert_equal true, User.default_admin_account_changed?
809 end
809 end
810
810
811 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
811 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
812 user = User.find_by_login("admin")
812 user = User.find_by_login("admin")
813 user.destroy
813 user.destroy
814
814
815 assert_equal true, User.default_admin_account_changed?
815 assert_equal true, User.default_admin_account_changed?
816 end
816 end
817
817
818 def test_membership_with_project_should_return_membership
818 def test_membership_with_project_should_return_membership
819 project = Project.find(1)
819 project = Project.find(1)
820
820
821 membership = @jsmith.membership(project)
821 membership = @jsmith.membership(project)
822 assert_kind_of Member, membership
822 assert_kind_of Member, membership
823 assert_equal @jsmith, membership.user
823 assert_equal @jsmith, membership.user
824 assert_equal project, membership.project
824 assert_equal project, membership.project
825 end
825 end
826
826
827 def test_membership_with_project_id_should_return_membership
827 def test_membership_with_project_id_should_return_membership
828 project = Project.find(1)
828 project = Project.find(1)
829
829
830 membership = @jsmith.membership(1)
830 membership = @jsmith.membership(1)
831 assert_kind_of Member, membership
831 assert_kind_of Member, membership
832 assert_equal @jsmith, membership.user
832 assert_equal @jsmith, membership.user
833 assert_equal project, membership.project
833 assert_equal project, membership.project
834 end
834 end
835
835
836 def test_membership_for_non_member_should_return_nil
836 def test_membership_for_non_member_should_return_nil
837 project = Project.find(1)
837 project = Project.find(1)
838
838
839 user = User.generate!
839 user = User.generate!
840 membership = user.membership(1)
840 membership = user.membership(1)
841 assert_nil membership
841 assert_nil membership
842 end
842 end
843
843
844 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
844 def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member
845 roles = @jsmith.roles_for_project(Project.find(1))
845 roles = @jsmith.roles_for_project(Project.find(1))
846 assert_kind_of Role, roles.first
846 assert_kind_of Role, roles.first
847 assert_equal ["Manager"], roles.map(&:name)
847 assert_equal ["Manager"], roles.map(&:name)
848 end
848 end
849
849
850 def test_roles_for_project_with_member_on_private_project_should_return_roles
850 def test_roles_for_project_with_member_on_private_project_should_return_roles
851 Project.find(1).update_attribute :is_public, false
851 Project.find(1).update_attribute :is_public, false
852
852
853 roles = @jsmith.roles_for_project(Project.find(1))
853 roles = @jsmith.roles_for_project(Project.find(1))
854 assert_kind_of Role, roles.first
854 assert_kind_of Role, roles.first
855 assert_equal ["Manager"], roles.map(&:name)
855 assert_equal ["Manager"], roles.map(&:name)
856 end
856 end
857
857
858 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
858 def test_roles_for_project_with_non_member_with_public_project_should_return_non_member
859 set_language_if_valid 'en'
859 set_language_if_valid 'en'
860 roles = User.find(8).roles_for_project(Project.find(1))
860 roles = User.find(8).roles_for_project(Project.find(1))
861 assert_equal ["Non member"], roles.map(&:name)
861 assert_equal ["Non member"], roles.map(&:name)
862 end
862 end
863
863
864 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
864 def test_roles_for_project_with_non_member_with_public_project_and_override_should_return_override_roles
865 project = Project.find(1)
865 project = Project.find(1)
866 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
866 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
867 roles = User.find(8).roles_for_project(project)
867 roles = User.find(8).roles_for_project(project)
868 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
868 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
869 end
869 end
870
870
871 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
871 def test_roles_for_project_with_non_member_with_private_project_should_return_no_roles
872 Project.find(1).update_attribute :is_public, false
872 Project.find(1).update_attribute :is_public, false
873
873
874 roles = User.find(8).roles_for_project(Project.find(1))
874 roles = User.find(8).roles_for_project(Project.find(1))
875 assert_equal [], roles.map(&:name)
875 assert_equal [], roles.map(&:name)
876 end
876 end
877
877
878 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
878 def test_roles_for_project_with_non_member_with_private_project_and_override_should_return_no_roles
879 project = Project.find(1)
879 project = Project.find(1)
880 project.update_attribute :is_public, false
880 project.update_attribute :is_public, false
881 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
881 Member.create!(:project => project, :principal => Group.non_member, :role_ids => [1, 2])
882 roles = User.find(8).roles_for_project(project)
882 roles = User.find(8).roles_for_project(project)
883 assert_equal [], roles.map(&:name).sort
883 assert_equal [], roles.map(&:name).sort
884 end
884 end
885
885
886 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
886 def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous
887 set_language_if_valid 'en'
887 set_language_if_valid 'en'
888 roles = User.anonymous.roles_for_project(Project.find(1))
888 roles = User.anonymous.roles_for_project(Project.find(1))
889 assert_equal ["Anonymous"], roles.map(&:name)
889 assert_equal ["Anonymous"], roles.map(&:name)
890 end
890 end
891
891
892 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
892 def test_roles_for_project_with_anonymous_with_public_project_and_override_should_return_override_roles
893 project = Project.find(1)
893 project = Project.find(1)
894 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
894 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
895 roles = User.anonymous.roles_for_project(project)
895 roles = User.anonymous.roles_for_project(project)
896 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
896 assert_equal ["Developer", "Manager"], roles.map(&:name).sort
897 end
897 end
898
898
899 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
899 def test_roles_for_project_with_anonymous_with_private_project_should_return_no_roles
900 Project.find(1).update_attribute :is_public, false
900 Project.find(1).update_attribute :is_public, false
901
901
902 roles = User.anonymous.roles_for_project(Project.find(1))
902 roles = User.anonymous.roles_for_project(Project.find(1))
903 assert_equal [], roles.map(&:name)
903 assert_equal [], roles.map(&:name)
904 end
904 end
905
905
906 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
906 def test_roles_for_project_with_anonymous_with_private_project_and_override_should_return_no_roles
907 project = Project.find(1)
907 project = Project.find(1)
908 project.update_attribute :is_public, false
908 project.update_attribute :is_public, false
909 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
909 Member.create!(:project => project, :principal => Group.anonymous, :role_ids => [1, 2])
910 roles = User.anonymous.roles_for_project(project)
910 roles = User.anonymous.roles_for_project(project)
911 assert_equal [], roles.map(&:name).sort
911 assert_equal [], roles.map(&:name).sort
912 end
912 end
913
913
914 def test_projects_by_role_for_user_with_role
914 def test_projects_by_role_for_user_with_role
915 user = User.find(2)
915 user = User.find(2)
916 assert_kind_of Hash, user.projects_by_role
916 assert_kind_of Hash, user.projects_by_role
917 assert_equal 2, user.projects_by_role.size
917 assert_equal 2, user.projects_by_role.size
918 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
918 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
919 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
919 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
920 end
920 end
921
921
922 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
922 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
923 user = User.find(2)
923 user = User.find(2)
924 assert_equal [], user.projects_by_role[Role.find(3)]
924 assert_equal [], user.projects_by_role[Role.find(3)]
925 # should not update the hash
925 # should not update the hash
926 assert_nil user.projects_by_role.values.detect(&:blank?)
926 assert_nil user.projects_by_role.values.detect(&:blank?)
927 end
927 end
928
928
929 def test_projects_by_role_for_user_with_no_role
929 def test_projects_by_role_for_user_with_no_role
930 user = User.generate!
930 user = User.generate!
931 assert_equal({}, user.projects_by_role)
931 assert_equal({}, user.projects_by_role)
932 end
932 end
933
933
934 def test_projects_by_role_for_anonymous
934 def test_projects_by_role_for_anonymous
935 assert_equal({}, User.anonymous.projects_by_role)
935 assert_equal({}, User.anonymous.projects_by_role)
936 end
936 end
937
937
938 def test_valid_notification_options
938 def test_valid_notification_options
939 # without memberships
939 # without memberships
940 assert_equal 5, User.find(7).valid_notification_options.size
940 assert_equal 5, User.find(7).valid_notification_options.size
941 # with memberships
941 # with memberships
942 assert_equal 6, User.find(2).valid_notification_options.size
942 assert_equal 6, User.find(2).valid_notification_options.size
943 end
943 end
944
944
945 def test_valid_notification_options_class_method
945 def test_valid_notification_options_class_method
946 assert_equal 5, User.valid_notification_options.size
946 assert_equal 5, User.valid_notification_options.size
947 assert_equal 5, User.valid_notification_options(User.find(7)).size
947 assert_equal 5, User.valid_notification_options(User.find(7)).size
948 assert_equal 6, User.valid_notification_options(User.find(2)).size
948 assert_equal 6, User.valid_notification_options(User.find(2)).size
949 end
949 end
950
950
951 def test_notified_project_ids_setter_should_coerce_to_unique_integer_array
952 @jsmith.notified_project_ids = ["1", "123", "2u", "wrong", "12", 6, 12, -35, ""]
953 assert_equal [1, 123, 2, 12, 6], @jsmith.notified_projects_ids
954 end
955
951 def test_mail_notification_all
956 def test_mail_notification_all
952 @jsmith.mail_notification = 'all'
957 @jsmith.mail_notification = 'all'
953 @jsmith.notified_project_ids = []
958 @jsmith.notified_project_ids = []
954 @jsmith.save
959 @jsmith.save
955 @jsmith.reload
960 @jsmith.reload
956 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
961 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
957 end
962 end
958
963
959 def test_mail_notification_selected
964 def test_mail_notification_selected
960 @jsmith.mail_notification = 'selected'
965 @jsmith.mail_notification = 'selected'
961 @jsmith.notified_project_ids = [1]
966 @jsmith.notified_project_ids = [1]
962 @jsmith.save
967 @jsmith.save
963 @jsmith.reload
968 @jsmith.reload
964 assert Project.find(1).recipients.include?(@jsmith.mail)
969 assert Project.find(1).recipients.include?(@jsmith.mail)
965 end
970 end
966
971
967 def test_mail_notification_only_my_events
972 def test_mail_notification_only_my_events
968 @jsmith.mail_notification = 'only_my_events'
973 @jsmith.mail_notification = 'only_my_events'
969 @jsmith.notified_project_ids = []
974 @jsmith.notified_project_ids = []
970 @jsmith.save
975 @jsmith.save
971 @jsmith.reload
976 @jsmith.reload
972 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
977 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
973 end
978 end
974
979
975 def test_comments_sorting_preference
980 def test_comments_sorting_preference
976 assert !@jsmith.wants_comments_in_reverse_order?
981 assert !@jsmith.wants_comments_in_reverse_order?
977 @jsmith.pref.comments_sorting = 'asc'
982 @jsmith.pref.comments_sorting = 'asc'
978 assert !@jsmith.wants_comments_in_reverse_order?
983 assert !@jsmith.wants_comments_in_reverse_order?
979 @jsmith.pref.comments_sorting = 'desc'
984 @jsmith.pref.comments_sorting = 'desc'
980 assert @jsmith.wants_comments_in_reverse_order?
985 assert @jsmith.wants_comments_in_reverse_order?
981 end
986 end
982
987
983 def test_find_by_mail_should_be_case_insensitive
988 def test_find_by_mail_should_be_case_insensitive
984 u = User.find_by_mail('JSmith@somenet.foo')
989 u = User.find_by_mail('JSmith@somenet.foo')
985 assert_not_nil u
990 assert_not_nil u
986 assert_equal 'jsmith@somenet.foo', u.mail
991 assert_equal 'jsmith@somenet.foo', u.mail
987 end
992 end
988
993
989 def test_random_password
994 def test_random_password
990 u = User.new
995 u = User.new
991 u.random_password
996 u.random_password
992 assert !u.password.blank?
997 assert !u.password.blank?
993 assert !u.password_confirmation.blank?
998 assert !u.password_confirmation.blank?
994 end
999 end
995
1000
996 test "#change_password_allowed? should be allowed if no auth source is set" do
1001 test "#change_password_allowed? should be allowed if no auth source is set" do
997 user = User.generate!
1002 user = User.generate!
998 assert user.change_password_allowed?
1003 assert user.change_password_allowed?
999 end
1004 end
1000
1005
1001 test "#change_password_allowed? should delegate to the auth source" do
1006 test "#change_password_allowed? should delegate to the auth source" do
1002 user = User.generate!
1007 user = User.generate!
1003
1008
1004 allowed_auth_source = AuthSource.generate!
1009 allowed_auth_source = AuthSource.generate!
1005 def allowed_auth_source.allow_password_changes?; true; end
1010 def allowed_auth_source.allow_password_changes?; true; end
1006
1011
1007 denied_auth_source = AuthSource.generate!
1012 denied_auth_source = AuthSource.generate!
1008 def denied_auth_source.allow_password_changes?; false; end
1013 def denied_auth_source.allow_password_changes?; false; end
1009
1014
1010 assert user.change_password_allowed?
1015 assert user.change_password_allowed?
1011
1016
1012 user.auth_source = allowed_auth_source
1017 user.auth_source = allowed_auth_source
1013 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1018 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
1014
1019
1015 user.auth_source = denied_auth_source
1020 user.auth_source = denied_auth_source
1016 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1021 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
1017 end
1022 end
1018
1023
1019 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1024 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
1020 with_settings :unsubscribe => '1' do
1025 with_settings :unsubscribe => '1' do
1021 assert_equal true, User.find(2).own_account_deletable?
1026 assert_equal true, User.find(2).own_account_deletable?
1022 end
1027 end
1023 end
1028 end
1024
1029
1025 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1030 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
1026 with_settings :unsubscribe => '0' do
1031 with_settings :unsubscribe => '0' do
1027 assert_equal false, User.find(2).own_account_deletable?
1032 assert_equal false, User.find(2).own_account_deletable?
1028 end
1033 end
1029 end
1034 end
1030
1035
1031 def test_own_account_deletable_should_be_false_for_a_single_admin
1036 def test_own_account_deletable_should_be_false_for_a_single_admin
1032 User.delete_all(["admin = ? AND id <> ?", true, 1])
1037 User.delete_all(["admin = ? AND id <> ?", true, 1])
1033
1038
1034 with_settings :unsubscribe => '1' do
1039 with_settings :unsubscribe => '1' do
1035 assert_equal false, User.find(1).own_account_deletable?
1040 assert_equal false, User.find(1).own_account_deletable?
1036 end
1041 end
1037 end
1042 end
1038
1043
1039 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1044 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
1040 User.generate! do |user|
1045 User.generate! do |user|
1041 user.admin = true
1046 user.admin = true
1042 end
1047 end
1043
1048
1044 with_settings :unsubscribe => '1' do
1049 with_settings :unsubscribe => '1' do
1045 assert_equal true, User.find(1).own_account_deletable?
1050 assert_equal true, User.find(1).own_account_deletable?
1046 end
1051 end
1047 end
1052 end
1048
1053
1049 context "#allowed_to?" do
1054 context "#allowed_to?" do
1050 context "with a unique project" do
1055 context "with a unique project" do
1051 should "return false if project is archived" do
1056 should "return false if project is archived" do
1052 project = Project.find(1)
1057 project = Project.find(1)
1053 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
1058 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
1054 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
1059 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
1055 end
1060 end
1056
1061
1057 should "return false for write action if project is closed" do
1062 should "return false for write action if project is closed" do
1058 project = Project.find(1)
1063 project = Project.find(1)
1059 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
1064 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
1060 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
1065 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
1061 end
1066 end
1062
1067
1063 should "return true for read action if project is closed" do
1068 should "return true for read action if project is closed" do
1064 project = Project.find(1)
1069 project = Project.find(1)
1065 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
1070 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
1066 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
1071 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
1067 end
1072 end
1068
1073
1069 should "return false if related module is disabled" do
1074 should "return false if related module is disabled" do
1070 project = Project.find(1)
1075 project = Project.find(1)
1071 project.enabled_module_names = ["issue_tracking"]
1076 project.enabled_module_names = ["issue_tracking"]
1072 assert_equal true, @admin.allowed_to?(:add_issues, project)
1077 assert_equal true, @admin.allowed_to?(:add_issues, project)
1073 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1078 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
1074 end
1079 end
1075
1080
1076 should "authorize nearly everything for admin users" do
1081 should "authorize nearly everything for admin users" do
1077 project = Project.find(1)
1082 project = Project.find(1)
1078 assert ! @admin.member_of?(project)
1083 assert ! @admin.member_of?(project)
1079 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1084 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
1080 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1085 assert_equal true, @admin.allowed_to?(p.to_sym, project)
1081 end
1086 end
1082 end
1087 end
1083
1088
1084 should "authorize normal users depending on their roles" do
1089 should "authorize normal users depending on their roles" do
1085 project = Project.find(1)
1090 project = Project.find(1)
1086 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1091 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
1087 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1092 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
1088 end
1093 end
1089 end
1094 end
1090
1095
1091 context "with multiple projects" do
1096 context "with multiple projects" do
1092 should "return false if array is empty" do
1097 should "return false if array is empty" do
1093 assert_equal false, @admin.allowed_to?(:view_project, [])
1098 assert_equal false, @admin.allowed_to?(:view_project, [])
1094 end
1099 end
1095
1100
1096 should "return true only if user has permission on all these projects" do
1101 should "return true only if user has permission on all these projects" do
1097 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1102 assert_equal true, @admin.allowed_to?(:view_project, Project.all.to_a)
1098 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1103 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all.to_a) #cannot see Project(2)
1099 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1104 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects.to_a) #Manager or Developer everywhere
1100 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1105 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects.to_a) #Dev cannot delete_issue_watchers
1101 end
1106 end
1102
1107
1103 should "behave correctly with arrays of 1 project" do
1108 should "behave correctly with arrays of 1 project" do
1104 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
1109 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
1105 end
1110 end
1106 end
1111 end
1107
1112
1108 context "with options[:global]" do
1113 context "with options[:global]" do
1109 should "authorize if user has at least one role that has this permission" do
1114 should "authorize if user has at least one role that has this permission" do
1110 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1115 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1111 @anonymous = User.find(6)
1116 @anonymous = User.find(6)
1112 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1117 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
1113 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1118 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
1114 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1119 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
1115 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1120 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
1116 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1121 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
1117 end
1122 end
1118 end
1123 end
1119 end
1124 end
1120
1125
1121 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1126 # this is just a proxy method, the test only calls it to ensure it doesn't break trivially
1122 context "#allowed_to_globally?" do
1127 context "#allowed_to_globally?" do
1123 should "proxy to #allowed_to? and reflect global permissions" do
1128 should "proxy to #allowed_to? and reflect global permissions" do
1124 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1129 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
1125 @anonymous = User.find(6)
1130 @anonymous = User.find(6)
1126 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1131 assert_equal true, @jsmith.allowed_to_globally?(:delete_issue_watchers)
1127 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1132 assert_equal false, @dlopper2.allowed_to_globally?(:delete_issue_watchers)
1128 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1133 assert_equal true, @dlopper2.allowed_to_globally?(:add_issues)
1129 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1134 assert_equal false, @anonymous.allowed_to_globally?(:add_issues)
1130 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1135 assert_equal true, @anonymous.allowed_to_globally?(:view_issues)
1131 end
1136 end
1132 end
1137 end
1133
1138
1134 context "User#notify_about?" do
1139 context "User#notify_about?" do
1135 context "Issues" do
1140 context "Issues" do
1136 setup do
1141 setup do
1137 @project = Project.find(1)
1142 @project = Project.find(1)
1138 @author = User.generate!
1143 @author = User.generate!
1139 @assignee = User.generate!
1144 @assignee = User.generate!
1140 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1145 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1141 end
1146 end
1142
1147
1143 should "be true for a user with :all" do
1148 should "be true for a user with :all" do
1144 @author.update_attribute(:mail_notification, 'all')
1149 @author.update_attribute(:mail_notification, 'all')
1145 assert @author.notify_about?(@issue)
1150 assert @author.notify_about?(@issue)
1146 end
1151 end
1147
1152
1148 should "be false for a user with :none" do
1153 should "be false for a user with :none" do
1149 @author.update_attribute(:mail_notification, 'none')
1154 @author.update_attribute(:mail_notification, 'none')
1150 assert ! @author.notify_about?(@issue)
1155 assert ! @author.notify_about?(@issue)
1151 end
1156 end
1152
1157
1153 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
1158 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
1154 @user = User.generate!(:mail_notification => 'only_my_events')
1159 @user = User.generate!(:mail_notification => 'only_my_events')
1155 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1160 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1156 assert ! @user.notify_about?(@issue)
1161 assert ! @user.notify_about?(@issue)
1157 end
1162 end
1158
1163
1159 should "be true for a user with :only_my_events and is the author" do
1164 should "be true for a user with :only_my_events and is the author" do
1160 @author.update_attribute(:mail_notification, 'only_my_events')
1165 @author.update_attribute(:mail_notification, 'only_my_events')
1161 assert @author.notify_about?(@issue)
1166 assert @author.notify_about?(@issue)
1162 end
1167 end
1163
1168
1164 should "be true for a user with :only_my_events and is the assignee" do
1169 should "be true for a user with :only_my_events and is the assignee" do
1165 @assignee.update_attribute(:mail_notification, 'only_my_events')
1170 @assignee.update_attribute(:mail_notification, 'only_my_events')
1166 assert @assignee.notify_about?(@issue)
1171 assert @assignee.notify_about?(@issue)
1167 end
1172 end
1168
1173
1169 should "be true for a user with :only_assigned and is the assignee" do
1174 should "be true for a user with :only_assigned and is the assignee" do
1170 @assignee.update_attribute(:mail_notification, 'only_assigned')
1175 @assignee.update_attribute(:mail_notification, 'only_assigned')
1171 assert @assignee.notify_about?(@issue)
1176 assert @assignee.notify_about?(@issue)
1172 end
1177 end
1173
1178
1174 should "be false for a user with :only_assigned and is not the assignee" do
1179 should "be false for a user with :only_assigned and is not the assignee" do
1175 @author.update_attribute(:mail_notification, 'only_assigned')
1180 @author.update_attribute(:mail_notification, 'only_assigned')
1176 assert ! @author.notify_about?(@issue)
1181 assert ! @author.notify_about?(@issue)
1177 end
1182 end
1178
1183
1179 should "be true for a user with :only_owner and is the author" do
1184 should "be true for a user with :only_owner and is the author" do
1180 @author.update_attribute(:mail_notification, 'only_owner')
1185 @author.update_attribute(:mail_notification, 'only_owner')
1181 assert @author.notify_about?(@issue)
1186 assert @author.notify_about?(@issue)
1182 end
1187 end
1183
1188
1184 should "be false for a user with :only_owner and is not the author" do
1189 should "be false for a user with :only_owner and is not the author" do
1185 @assignee.update_attribute(:mail_notification, 'only_owner')
1190 @assignee.update_attribute(:mail_notification, 'only_owner')
1186 assert ! @assignee.notify_about?(@issue)
1191 assert ! @assignee.notify_about?(@issue)
1187 end
1192 end
1188
1193
1189 should "be true for a user with :selected and is the author" do
1194 should "be true for a user with :selected and is the author" do
1190 @author.update_attribute(:mail_notification, 'selected')
1195 @author.update_attribute(:mail_notification, 'selected')
1191 assert @author.notify_about?(@issue)
1196 assert @author.notify_about?(@issue)
1192 end
1197 end
1193
1198
1194 should "be true for a user with :selected and is the assignee" do
1199 should "be true for a user with :selected and is the assignee" do
1195 @assignee.update_attribute(:mail_notification, 'selected')
1200 @assignee.update_attribute(:mail_notification, 'selected')
1196 assert @assignee.notify_about?(@issue)
1201 assert @assignee.notify_about?(@issue)
1197 end
1202 end
1198
1203
1199 should "be false for a user with :selected and is not the author or assignee" do
1204 should "be false for a user with :selected and is not the author or assignee" do
1200 @user = User.generate!(:mail_notification => 'selected')
1205 @user = User.generate!(:mail_notification => 'selected')
1201 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1206 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1202 assert ! @user.notify_about?(@issue)
1207 assert ! @user.notify_about?(@issue)
1203 end
1208 end
1204 end
1209 end
1205 end
1210 end
1206
1211
1207 def test_notify_about_news
1212 def test_notify_about_news
1208 user = User.generate!
1213 user = User.generate!
1209 news = News.new
1214 news = News.new
1210
1215
1211 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1216 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1212 user.mail_notification = option
1217 user.mail_notification = option
1213 assert_equal (option != 'none'), user.notify_about?(news)
1218 assert_equal (option != 'none'), user.notify_about?(news)
1214 end
1219 end
1215 end
1220 end
1216
1221
1217 def test_salt_unsalted_passwords
1222 def test_salt_unsalted_passwords
1218 # Restore a user with an unsalted password
1223 # Restore a user with an unsalted password
1219 user = User.find(1)
1224 user = User.find(1)
1220 user.salt = nil
1225 user.salt = nil
1221 user.hashed_password = User.hash_password("unsalted")
1226 user.hashed_password = User.hash_password("unsalted")
1222 user.save!
1227 user.save!
1223
1228
1224 User.salt_unsalted_passwords!
1229 User.salt_unsalted_passwords!
1225
1230
1226 user.reload
1231 user.reload
1227 # Salt added
1232 # Salt added
1228 assert !user.salt.blank?
1233 assert !user.salt.blank?
1229 # Password still valid
1234 # Password still valid
1230 assert user.check_password?("unsalted")
1235 assert user.check_password?("unsalted")
1231 assert_equal user, User.try_to_login(user.login, "unsalted")
1236 assert_equal user, User.try_to_login(user.login, "unsalted")
1232 end
1237 end
1233
1238
1234 if Object.const_defined?(:OpenID)
1239 if Object.const_defined?(:OpenID)
1235 def test_setting_identity_url
1240 def test_setting_identity_url
1236 normalized_open_id_url = 'http://example.com/'
1241 normalized_open_id_url = 'http://example.com/'
1237 u = User.new( :identity_url => 'http://example.com/' )
1242 u = User.new( :identity_url => 'http://example.com/' )
1238 assert_equal normalized_open_id_url, u.identity_url
1243 assert_equal normalized_open_id_url, u.identity_url
1239 end
1244 end
1240
1245
1241 def test_setting_identity_url_without_trailing_slash
1246 def test_setting_identity_url_without_trailing_slash
1242 normalized_open_id_url = 'http://example.com/'
1247 normalized_open_id_url = 'http://example.com/'
1243 u = User.new( :identity_url => 'http://example.com' )
1248 u = User.new( :identity_url => 'http://example.com' )
1244 assert_equal normalized_open_id_url, u.identity_url
1249 assert_equal normalized_open_id_url, u.identity_url
1245 end
1250 end
1246
1251
1247 def test_setting_identity_url_without_protocol
1252 def test_setting_identity_url_without_protocol
1248 normalized_open_id_url = 'http://example.com/'
1253 normalized_open_id_url = 'http://example.com/'
1249 u = User.new( :identity_url => 'example.com' )
1254 u = User.new( :identity_url => 'example.com' )
1250 assert_equal normalized_open_id_url, u.identity_url
1255 assert_equal normalized_open_id_url, u.identity_url
1251 end
1256 end
1252
1257
1253 def test_setting_blank_identity_url
1258 def test_setting_blank_identity_url
1254 u = User.new( :identity_url => 'example.com' )
1259 u = User.new( :identity_url => 'example.com' )
1255 u.identity_url = ''
1260 u.identity_url = ''
1256 assert u.identity_url.blank?
1261 assert u.identity_url.blank?
1257 end
1262 end
1258
1263
1259 def test_setting_invalid_identity_url
1264 def test_setting_invalid_identity_url
1260 u = User.new( :identity_url => 'this is not an openid url' )
1265 u = User.new( :identity_url => 'this is not an openid url' )
1261 assert u.identity_url.blank?
1266 assert u.identity_url.blank?
1262 end
1267 end
1263 else
1268 else
1264 puts "Skipping openid tests."
1269 puts "Skipping openid tests."
1265 end
1270 end
1266 end
1271 end
General Comments 0
You need to be logged in to leave comments. Login now