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