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