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