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