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