##// END OF EJS Templates
Merged r3873 from trunk....
Jean-Philippe Lang -
r3760:ec526c1261ac
parent child
Show More
@@ -1,382 +1,386
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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
21
22 # Account statuses
22 # Account statuses
23 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
24 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
25 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
26 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
27
27
28 USER_FORMATS = {
28 USER_FORMATS = {
29 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :username => '#{login}'
33 :username => '#{login}'
34 }
34 }
35
35
36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 has_many :changesets, :dependent => :nullify
39 has_many :changesets, :dependent => :nullify
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
43 belongs_to :auth_source
43 belongs_to :auth_source
44
44
45 # Active non-anonymous users scope
45 # Active non-anonymous users scope
46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
47
47
48 acts_as_customizable
48 acts_as_customizable
49
49
50 attr_accessor :password, :password_confirmation
50 attr_accessor :password, :password_confirmation
51 attr_accessor :last_before_login_on
51 attr_accessor :last_before_login_on
52 # Prevents unauthorized assignments
52 # Prevents unauthorized assignments
53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
54
54
55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
58 # Login must contain lettres, numbers, underscores only
58 # Login must contain lettres, numbers, underscores only
59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
60 validates_length_of :login, :maximum => 30
60 validates_length_of :login, :maximum => 30
61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
62 validates_length_of :firstname, :lastname, :maximum => 30
62 validates_length_of :firstname, :lastname, :maximum => 30
63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
64 validates_length_of :mail, :maximum => 60, :allow_nil => true
64 validates_length_of :mail, :maximum => 60, :allow_nil => true
65 validates_confirmation_of :password, :allow_nil => true
65 validates_confirmation_of :password, :allow_nil => true
66
66
67 def before_create
67 def before_create
68 self.mail_notification = false
68 self.mail_notification = false
69 true
69 true
70 end
70 end
71
71
72 def before_save
72 def before_save
73 # update hashed_password if password was set
73 # update hashed_password if password was set
74 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
74 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
75 end
75 end
76
76
77 def reload(*args)
77 def reload(*args)
78 @name = nil
78 @name = nil
79 super
79 super
80 end
80 end
81
81
82 def mail=(arg)
83 write_attribute(:mail, arg.to_s.strip)
84 end
85
82 def identity_url=(url)
86 def identity_url=(url)
83 if url.blank?
87 if url.blank?
84 write_attribute(:identity_url, '')
88 write_attribute(:identity_url, '')
85 else
89 else
86 begin
90 begin
87 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
91 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
88 rescue OpenIdAuthentication::InvalidOpenId
92 rescue OpenIdAuthentication::InvalidOpenId
89 # Invlaid url, don't save
93 # Invlaid url, don't save
90 end
94 end
91 end
95 end
92 self.read_attribute(:identity_url)
96 self.read_attribute(:identity_url)
93 end
97 end
94
98
95 # Returns the user that matches provided login and password, or nil
99 # Returns the user that matches provided login and password, or nil
96 def self.try_to_login(login, password)
100 def self.try_to_login(login, password)
97 # Make sure no one can sign in with an empty password
101 # Make sure no one can sign in with an empty password
98 return nil if password.to_s.empty?
102 return nil if password.to_s.empty?
99 user = find_by_login(login)
103 user = find_by_login(login)
100 if user
104 if user
101 # user is already in local database
105 # user is already in local database
102 return nil if !user.active?
106 return nil if !user.active?
103 if user.auth_source
107 if user.auth_source
104 # user has an external authentication method
108 # user has an external authentication method
105 return nil unless user.auth_source.authenticate(login, password)
109 return nil unless user.auth_source.authenticate(login, password)
106 else
110 else
107 # authentication with local password
111 # authentication with local password
108 return nil unless User.hash_password(password) == user.hashed_password
112 return nil unless User.hash_password(password) == user.hashed_password
109 end
113 end
110 else
114 else
111 # user is not yet registered, try to authenticate with available sources
115 # user is not yet registered, try to authenticate with available sources
112 attrs = AuthSource.authenticate(login, password)
116 attrs = AuthSource.authenticate(login, password)
113 if attrs
117 if attrs
114 user = new(attrs)
118 user = new(attrs)
115 user.login = login
119 user.login = login
116 user.language = Setting.default_language
120 user.language = Setting.default_language
117 if user.save
121 if user.save
118 user.reload
122 user.reload
119 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
123 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
120 end
124 end
121 end
125 end
122 end
126 end
123 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
127 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
124 user
128 user
125 rescue => text
129 rescue => text
126 raise text
130 raise text
127 end
131 end
128
132
129 # Returns the user who matches the given autologin +key+ or nil
133 # Returns the user who matches the given autologin +key+ or nil
130 def self.try_to_autologin(key)
134 def self.try_to_autologin(key)
131 tokens = Token.find_all_by_action_and_value('autologin', key)
135 tokens = Token.find_all_by_action_and_value('autologin', key)
132 # Make sure there's only 1 token that matches the key
136 # Make sure there's only 1 token that matches the key
133 if tokens.size == 1
137 if tokens.size == 1
134 token = tokens.first
138 token = tokens.first
135 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
139 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
136 token.user.update_attribute(:last_login_on, Time.now)
140 token.user.update_attribute(:last_login_on, Time.now)
137 token.user
141 token.user
138 end
142 end
139 end
143 end
140 end
144 end
141
145
142 # Return user's full name for display
146 # Return user's full name for display
143 def name(formatter = nil)
147 def name(formatter = nil)
144 if formatter
148 if formatter
145 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
149 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
146 else
150 else
147 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
151 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
148 end
152 end
149 end
153 end
150
154
151 def active?
155 def active?
152 self.status == STATUS_ACTIVE
156 self.status == STATUS_ACTIVE
153 end
157 end
154
158
155 def registered?
159 def registered?
156 self.status == STATUS_REGISTERED
160 self.status == STATUS_REGISTERED
157 end
161 end
158
162
159 def locked?
163 def locked?
160 self.status == STATUS_LOCKED
164 self.status == STATUS_LOCKED
161 end
165 end
162
166
163 def check_password?(clear_password)
167 def check_password?(clear_password)
164 if auth_source_id.present?
168 if auth_source_id.present?
165 auth_source.authenticate(self.login, clear_password)
169 auth_source.authenticate(self.login, clear_password)
166 else
170 else
167 User.hash_password(clear_password) == self.hashed_password
171 User.hash_password(clear_password) == self.hashed_password
168 end
172 end
169 end
173 end
170
174
171 # Does the backend storage allow this user to change their password?
175 # Does the backend storage allow this user to change their password?
172 def change_password_allowed?
176 def change_password_allowed?
173 return true if auth_source_id.blank?
177 return true if auth_source_id.blank?
174 return auth_source.allow_password_changes?
178 return auth_source.allow_password_changes?
175 end
179 end
176
180
177 # Generate and set a random password. Useful for automated user creation
181 # Generate and set a random password. Useful for automated user creation
178 # Based on Token#generate_token_value
182 # Based on Token#generate_token_value
179 #
183 #
180 def random_password
184 def random_password
181 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
185 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
182 password = ''
186 password = ''
183 40.times { |i| password << chars[rand(chars.size-1)] }
187 40.times { |i| password << chars[rand(chars.size-1)] }
184 self.password = password
188 self.password = password
185 self.password_confirmation = password
189 self.password_confirmation = password
186 self
190 self
187 end
191 end
188
192
189 def pref
193 def pref
190 self.preference ||= UserPreference.new(:user => self)
194 self.preference ||= UserPreference.new(:user => self)
191 end
195 end
192
196
193 def time_zone
197 def time_zone
194 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
198 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
195 end
199 end
196
200
197 def wants_comments_in_reverse_order?
201 def wants_comments_in_reverse_order?
198 self.pref[:comments_sorting] == 'desc'
202 self.pref[:comments_sorting] == 'desc'
199 end
203 end
200
204
201 # Return user's RSS key (a 40 chars long string), used to access feeds
205 # Return user's RSS key (a 40 chars long string), used to access feeds
202 def rss_key
206 def rss_key
203 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
207 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
204 token.value
208 token.value
205 end
209 end
206
210
207 # Return user's API key (a 40 chars long string), used to access the API
211 # Return user's API key (a 40 chars long string), used to access the API
208 def api_key
212 def api_key
209 token = self.api_token || self.create_api_token(:action => 'api')
213 token = self.api_token || self.create_api_token(:action => 'api')
210 token.value
214 token.value
211 end
215 end
212
216
213 # Return an array of project ids for which the user has explicitly turned mail notifications on
217 # Return an array of project ids for which the user has explicitly turned mail notifications on
214 def notified_projects_ids
218 def notified_projects_ids
215 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
219 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
216 end
220 end
217
221
218 def notified_project_ids=(ids)
222 def notified_project_ids=(ids)
219 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
223 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
220 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
224 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
221 @notified_projects_ids = nil
225 @notified_projects_ids = nil
222 notified_projects_ids
226 notified_projects_ids
223 end
227 end
224
228
225 # Find a user account by matching the exact login and then a case-insensitive
229 # Find a user account by matching the exact login and then a case-insensitive
226 # version. Exact matches will be given priority.
230 # version. Exact matches will be given priority.
227 def self.find_by_login(login)
231 def self.find_by_login(login)
228 # force string comparison to be case sensitive on MySQL
232 # force string comparison to be case sensitive on MySQL
229 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
233 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
230
234
231 # First look for an exact match
235 # First look for an exact match
232 user = first(:conditions => ["#{type_cast} login = ?", login])
236 user = first(:conditions => ["#{type_cast} login = ?", login])
233 # Fail over to case-insensitive if none was found
237 # Fail over to case-insensitive if none was found
234 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
238 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
235 end
239 end
236
240
237 def self.find_by_rss_key(key)
241 def self.find_by_rss_key(key)
238 token = Token.find_by_value(key)
242 token = Token.find_by_value(key)
239 token && token.user.active? ? token.user : nil
243 token && token.user.active? ? token.user : nil
240 end
244 end
241
245
242 def self.find_by_api_key(key)
246 def self.find_by_api_key(key)
243 token = Token.find_by_action_and_value('api', key)
247 token = Token.find_by_action_and_value('api', key)
244 token && token.user.active? ? token.user : nil
248 token && token.user.active? ? token.user : nil
245 end
249 end
246
250
247 # Makes find_by_mail case-insensitive
251 # Makes find_by_mail case-insensitive
248 def self.find_by_mail(mail)
252 def self.find_by_mail(mail)
249 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
253 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
250 end
254 end
251
255
252 def to_s
256 def to_s
253 name
257 name
254 end
258 end
255
259
256 # Returns the current day according to user's time zone
260 # Returns the current day according to user's time zone
257 def today
261 def today
258 if time_zone.nil?
262 if time_zone.nil?
259 Date.today
263 Date.today
260 else
264 else
261 Time.now.in_time_zone(time_zone).to_date
265 Time.now.in_time_zone(time_zone).to_date
262 end
266 end
263 end
267 end
264
268
265 def logged?
269 def logged?
266 true
270 true
267 end
271 end
268
272
269 def anonymous?
273 def anonymous?
270 !logged?
274 !logged?
271 end
275 end
272
276
273 # Return user's roles for project
277 # Return user's roles for project
274 def roles_for_project(project)
278 def roles_for_project(project)
275 roles = []
279 roles = []
276 # No role on archived projects
280 # No role on archived projects
277 return roles unless project && project.active?
281 return roles unless project && project.active?
278 if logged?
282 if logged?
279 # Find project membership
283 # Find project membership
280 membership = memberships.detect {|m| m.project_id == project.id}
284 membership = memberships.detect {|m| m.project_id == project.id}
281 if membership
285 if membership
282 roles = membership.roles
286 roles = membership.roles
283 else
287 else
284 @role_non_member ||= Role.non_member
288 @role_non_member ||= Role.non_member
285 roles << @role_non_member
289 roles << @role_non_member
286 end
290 end
287 else
291 else
288 @role_anonymous ||= Role.anonymous
292 @role_anonymous ||= Role.anonymous
289 roles << @role_anonymous
293 roles << @role_anonymous
290 end
294 end
291 roles
295 roles
292 end
296 end
293
297
294 # Return true if the user is a member of project
298 # Return true if the user is a member of project
295 def member_of?(project)
299 def member_of?(project)
296 !roles_for_project(project).detect {|role| role.member?}.nil?
300 !roles_for_project(project).detect {|role| role.member?}.nil?
297 end
301 end
298
302
299 # Return true if the user is allowed to do the specified action on project
303 # Return true if the user is allowed to do the specified action on project
300 # action can be:
304 # action can be:
301 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
305 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
302 # * a permission Symbol (eg. :edit_project)
306 # * a permission Symbol (eg. :edit_project)
303 def allowed_to?(action, project, options={})
307 def allowed_to?(action, project, options={})
304 if project
308 if project
305 # No action allowed on archived projects
309 # No action allowed on archived projects
306 return false unless project.active?
310 return false unless project.active?
307 # No action allowed on disabled modules
311 # No action allowed on disabled modules
308 return false unless project.allows_to?(action)
312 return false unless project.allows_to?(action)
309 # Admin users are authorized for anything else
313 # Admin users are authorized for anything else
310 return true if admin?
314 return true if admin?
311
315
312 roles = roles_for_project(project)
316 roles = roles_for_project(project)
313 return false unless roles
317 return false unless roles
314 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
318 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
315
319
316 elsif options[:global]
320 elsif options[:global]
317 # Admin users are always authorized
321 # Admin users are always authorized
318 return true if admin?
322 return true if admin?
319
323
320 # authorize if user has at least one role that has this permission
324 # authorize if user has at least one role that has this permission
321 roles = memberships.collect {|m| m.roles}.flatten.uniq
325 roles = memberships.collect {|m| m.roles}.flatten.uniq
322 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
326 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
323 else
327 else
324 false
328 false
325 end
329 end
326 end
330 end
327
331
328 def self.current=(user)
332 def self.current=(user)
329 @current_user = user
333 @current_user = user
330 end
334 end
331
335
332 def self.current
336 def self.current
333 @current_user ||= User.anonymous
337 @current_user ||= User.anonymous
334 end
338 end
335
339
336 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
340 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
337 # one anonymous user per database.
341 # one anonymous user per database.
338 def self.anonymous
342 def self.anonymous
339 anonymous_user = AnonymousUser.find(:first)
343 anonymous_user = AnonymousUser.find(:first)
340 if anonymous_user.nil?
344 if anonymous_user.nil?
341 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
345 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
342 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
346 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
343 end
347 end
344 anonymous_user
348 anonymous_user
345 end
349 end
346
350
347 protected
351 protected
348
352
349 def validate
353 def validate
350 # Password length validation based on setting
354 # Password length validation based on setting
351 if !password.nil? && password.size < Setting.password_min_length.to_i
355 if !password.nil? && password.size < Setting.password_min_length.to_i
352 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
356 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
353 end
357 end
354 end
358 end
355
359
356 private
360 private
357
361
358 # Return password digest
362 # Return password digest
359 def self.hash_password(clear_password)
363 def self.hash_password(clear_password)
360 Digest::SHA1.hexdigest(clear_password || "")
364 Digest::SHA1.hexdigest(clear_password || "")
361 end
365 end
362 end
366 end
363
367
364 class AnonymousUser < User
368 class AnonymousUser < User
365
369
366 def validate_on_create
370 def validate_on_create
367 # There should be only one AnonymousUser in the database
371 # There should be only one AnonymousUser in the database
368 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
372 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
369 end
373 end
370
374
371 def available_custom_fields
375 def available_custom_fields
372 []
376 []
373 end
377 end
374
378
375 # Overrides a few properties
379 # Overrides a few properties
376 def logged?; false end
380 def logged?; false end
377 def admin; false end
381 def admin; false end
378 def name(*args); I18n.t(:label_user_anonymous) end
382 def name(*args); I18n.t(:label_user_anonymous) end
379 def mail; nil end
383 def mail; nil end
380 def time_zone; nil end
384 def time_zone; nil end
381 def rss_key; nil end
385 def rss_key; nil end
382 end
386 end
@@ -1,387 +1,393
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
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
22
23 def setup
23 def setup
24 @admin = User.find(1)
24 @admin = User.find(1)
25 @jsmith = User.find(2)
25 @jsmith = User.find(2)
26 @dlopper = User.find(3)
26 @dlopper = User.find(3)
27 end
27 end
28
28
29 test 'object_daddy creation' do
29 test 'object_daddy creation' do
30 User.generate_with_protected!(:firstname => 'Testing connection')
30 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
33 end
33 end
34
34
35 def test_truth
35 def test_truth
36 assert_kind_of User, @jsmith
36 assert_kind_of User, @jsmith
37 end
37 end
38
39 def test_mail_should_be_stripped
40 u = User.new
41 u.mail = " foo@bar.com "
42 assert_equal "foo@bar.com", u.mail
43 end
38
44
39 def test_create
45 def test_create
40 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
41
47
42 user.login = "jsmith"
48 user.login = "jsmith"
43 user.password, user.password_confirmation = "password", "password"
49 user.password, user.password_confirmation = "password", "password"
44 # login uniqueness
50 # login uniqueness
45 assert !user.save
51 assert !user.save
46 assert_equal 1, user.errors.count
52 assert_equal 1, user.errors.count
47
53
48 user.login = "newuser"
54 user.login = "newuser"
49 user.password, user.password_confirmation = "passwd", "password"
55 user.password, user.password_confirmation = "passwd", "password"
50 # password confirmation
56 # password confirmation
51 assert !user.save
57 assert !user.save
52 assert_equal 1, user.errors.count
58 assert_equal 1, user.errors.count
53
59
54 user.password, user.password_confirmation = "password", "password"
60 user.password, user.password_confirmation = "password", "password"
55 assert user.save
61 assert user.save
56 end
62 end
57
63
58 context "User.login" do
64 context "User.login" do
59 should "be case-insensitive." do
65 should "be case-insensitive." do
60 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
66 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
61 u.login = 'newuser'
67 u.login = 'newuser'
62 u.password, u.password_confirmation = "password", "password"
68 u.password, u.password_confirmation = "password", "password"
63 assert u.save
69 assert u.save
64
70
65 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
71 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
66 u.login = 'NewUser'
72 u.login = 'NewUser'
67 u.password, u.password_confirmation = "password", "password"
73 u.password, u.password_confirmation = "password", "password"
68 assert !u.save
74 assert !u.save
69 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
75 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
70 end
76 end
71 end
77 end
72
78
73 def test_mail_uniqueness_should_not_be_case_sensitive
79 def test_mail_uniqueness_should_not_be_case_sensitive
74 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
80 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
75 u.login = 'newuser1'
81 u.login = 'newuser1'
76 u.password, u.password_confirmation = "password", "password"
82 u.password, u.password_confirmation = "password", "password"
77 assert u.save
83 assert u.save
78
84
79 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
85 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
80 u.login = 'newuser2'
86 u.login = 'newuser2'
81 u.password, u.password_confirmation = "password", "password"
87 u.password, u.password_confirmation = "password", "password"
82 assert !u.save
88 assert !u.save
83 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
89 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
84 end
90 end
85
91
86 def test_update
92 def test_update
87 assert_equal "admin", @admin.login
93 assert_equal "admin", @admin.login
88 @admin.login = "john"
94 @admin.login = "john"
89 assert @admin.save, @admin.errors.full_messages.join("; ")
95 assert @admin.save, @admin.errors.full_messages.join("; ")
90 @admin.reload
96 @admin.reload
91 assert_equal "john", @admin.login
97 assert_equal "john", @admin.login
92 end
98 end
93
99
94 def test_destroy
100 def test_destroy
95 User.find(2).destroy
101 User.find(2).destroy
96 assert_nil User.find_by_id(2)
102 assert_nil User.find_by_id(2)
97 assert Member.find_all_by_user_id(2).empty?
103 assert Member.find_all_by_user_id(2).empty?
98 end
104 end
99
105
100 def test_validate
106 def test_validate
101 @admin.login = ""
107 @admin.login = ""
102 assert !@admin.save
108 assert !@admin.save
103 assert_equal 1, @admin.errors.count
109 assert_equal 1, @admin.errors.count
104 end
110 end
105
111
106 context "User#try_to_login" do
112 context "User#try_to_login" do
107 should "fall-back to case-insensitive if user login is not found as-typed." do
113 should "fall-back to case-insensitive if user login is not found as-typed." do
108 user = User.try_to_login("AdMin", "admin")
114 user = User.try_to_login("AdMin", "admin")
109 assert_kind_of User, user
115 assert_kind_of User, user
110 assert_equal "admin", user.login
116 assert_equal "admin", user.login
111 end
117 end
112
118
113 should "select the exact matching user first" do
119 should "select the exact matching user first" do
114 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
120 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
115 # bypass validations to make it appear like existing data
121 # bypass validations to make it appear like existing data
116 case_sensitive_user.update_attribute(:login, 'ADMIN')
122 case_sensitive_user.update_attribute(:login, 'ADMIN')
117
123
118 user = User.try_to_login("ADMIN", "admin")
124 user = User.try_to_login("ADMIN", "admin")
119 assert_kind_of User, user
125 assert_kind_of User, user
120 assert_equal "ADMIN", user.login
126 assert_equal "ADMIN", user.login
121
127
122 end
128 end
123 end
129 end
124
130
125 def test_password
131 def test_password
126 user = User.try_to_login("admin", "admin")
132 user = User.try_to_login("admin", "admin")
127 assert_kind_of User, user
133 assert_kind_of User, user
128 assert_equal "admin", user.login
134 assert_equal "admin", user.login
129 user.password = "hello"
135 user.password = "hello"
130 assert user.save
136 assert user.save
131
137
132 user = User.try_to_login("admin", "hello")
138 user = User.try_to_login("admin", "hello")
133 assert_kind_of User, user
139 assert_kind_of User, user
134 assert_equal "admin", user.login
140 assert_equal "admin", user.login
135 assert_equal User.hash_password("hello"), user.hashed_password
141 assert_equal User.hash_password("hello"), user.hashed_password
136 end
142 end
137
143
138 def test_name_format
144 def test_name_format
139 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
145 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
140 Setting.user_format = :firstname_lastname
146 Setting.user_format = :firstname_lastname
141 assert_equal 'John Smith', @jsmith.reload.name
147 assert_equal 'John Smith', @jsmith.reload.name
142 Setting.user_format = :username
148 Setting.user_format = :username
143 assert_equal 'jsmith', @jsmith.reload.name
149 assert_equal 'jsmith', @jsmith.reload.name
144 end
150 end
145
151
146 def test_lock
152 def test_lock
147 user = User.try_to_login("jsmith", "jsmith")
153 user = User.try_to_login("jsmith", "jsmith")
148 assert_equal @jsmith, user
154 assert_equal @jsmith, user
149
155
150 @jsmith.status = User::STATUS_LOCKED
156 @jsmith.status = User::STATUS_LOCKED
151 assert @jsmith.save
157 assert @jsmith.save
152
158
153 user = User.try_to_login("jsmith", "jsmith")
159 user = User.try_to_login("jsmith", "jsmith")
154 assert_equal nil, user
160 assert_equal nil, user
155 end
161 end
156
162
157 if ldap_configured?
163 if ldap_configured?
158 context "#try_to_login using LDAP" do
164 context "#try_to_login using LDAP" do
159 context "with failed connection to the LDAP server" do
165 context "with failed connection to the LDAP server" do
160 should "return nil" do
166 should "return nil" do
161 @auth_source = AuthSourceLdap.find(1)
167 @auth_source = AuthSourceLdap.find(1)
162 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
168 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
163
169
164 assert_equal nil, User.try_to_login('edavis', 'wrong')
170 assert_equal nil, User.try_to_login('edavis', 'wrong')
165 end
171 end
166 end
172 end
167
173
168 context "with an unsuccessful authentication" do
174 context "with an unsuccessful authentication" do
169 should "return nil" do
175 should "return nil" do
170 assert_equal nil, User.try_to_login('edavis', 'wrong')
176 assert_equal nil, User.try_to_login('edavis', 'wrong')
171 end
177 end
172 end
178 end
173
179
174 context "on the fly registration" do
180 context "on the fly registration" do
175 setup do
181 setup do
176 @auth_source = AuthSourceLdap.find(1)
182 @auth_source = AuthSourceLdap.find(1)
177 end
183 end
178
184
179 context "with a successful authentication" do
185 context "with a successful authentication" do
180 should "create a new user account if it doesn't exist" do
186 should "create a new user account if it doesn't exist" do
181 assert_difference('User.count') do
187 assert_difference('User.count') do
182 user = User.try_to_login('edavis', '123456')
188 user = User.try_to_login('edavis', '123456')
183 assert !user.admin?
189 assert !user.admin?
184 end
190 end
185 end
191 end
186
192
187 should "retrieve existing user" do
193 should "retrieve existing user" do
188 user = User.try_to_login('edavis', '123456')
194 user = User.try_to_login('edavis', '123456')
189 user.admin = true
195 user.admin = true
190 user.save!
196 user.save!
191
197
192 assert_no_difference('User.count') do
198 assert_no_difference('User.count') do
193 user = User.try_to_login('edavis', '123456')
199 user = User.try_to_login('edavis', '123456')
194 assert user.admin?
200 assert user.admin?
195 end
201 end
196 end
202 end
197 end
203 end
198 end
204 end
199 end
205 end
200
206
201 else
207 else
202 puts "Skipping LDAP tests."
208 puts "Skipping LDAP tests."
203 end
209 end
204
210
205 def test_create_anonymous
211 def test_create_anonymous
206 AnonymousUser.delete_all
212 AnonymousUser.delete_all
207 anon = User.anonymous
213 anon = User.anonymous
208 assert !anon.new_record?
214 assert !anon.new_record?
209 assert_kind_of AnonymousUser, anon
215 assert_kind_of AnonymousUser, anon
210 end
216 end
211
217
212 should_have_one :rss_token
218 should_have_one :rss_token
213
219
214 def test_rss_key
220 def test_rss_key
215 assert_nil @jsmith.rss_token
221 assert_nil @jsmith.rss_token
216 key = @jsmith.rss_key
222 key = @jsmith.rss_key
217 assert_equal 40, key.length
223 assert_equal 40, key.length
218
224
219 @jsmith.reload
225 @jsmith.reload
220 assert_equal key, @jsmith.rss_key
226 assert_equal key, @jsmith.rss_key
221 end
227 end
222
228
223
229
224 should_have_one :api_token
230 should_have_one :api_token
225
231
226 context "User#api_key" do
232 context "User#api_key" do
227 should "generate a new one if the user doesn't have one" do
233 should "generate a new one if the user doesn't have one" do
228 user = User.generate_with_protected!(:api_token => nil)
234 user = User.generate_with_protected!(:api_token => nil)
229 assert_nil user.api_token
235 assert_nil user.api_token
230
236
231 key = user.api_key
237 key = user.api_key
232 assert_equal 40, key.length
238 assert_equal 40, key.length
233 user.reload
239 user.reload
234 assert_equal key, user.api_key
240 assert_equal key, user.api_key
235 end
241 end
236
242
237 should "return the existing api token value" do
243 should "return the existing api token value" do
238 user = User.generate_with_protected!
244 user = User.generate_with_protected!
239 token = Token.generate!(:action => 'api')
245 token = Token.generate!(:action => 'api')
240 user.api_token = token
246 user.api_token = token
241 assert user.save
247 assert user.save
242
248
243 assert_equal token.value, user.api_key
249 assert_equal token.value, user.api_key
244 end
250 end
245 end
251 end
246
252
247 context "User#find_by_api_key" do
253 context "User#find_by_api_key" do
248 should "return nil if no matching key is found" do
254 should "return nil if no matching key is found" do
249 assert_nil User.find_by_api_key('zzzzzzzzz')
255 assert_nil User.find_by_api_key('zzzzzzzzz')
250 end
256 end
251
257
252 should "return nil if the key is found for an inactive user" do
258 should "return nil if the key is found for an inactive user" do
253 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
259 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
254 token = Token.generate!(:action => 'api')
260 token = Token.generate!(:action => 'api')
255 user.api_token = token
261 user.api_token = token
256 user.save
262 user.save
257
263
258 assert_nil User.find_by_api_key(token.value)
264 assert_nil User.find_by_api_key(token.value)
259 end
265 end
260
266
261 should "return the user if the key is found for an active user" do
267 should "return the user if the key is found for an active user" do
262 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
268 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
263 token = Token.generate!(:action => 'api')
269 token = Token.generate!(:action => 'api')
264 user.api_token = token
270 user.api_token = token
265 user.save
271 user.save
266
272
267 assert_equal user, User.find_by_api_key(token.value)
273 assert_equal user, User.find_by_api_key(token.value)
268 end
274 end
269 end
275 end
270
276
271 def test_roles_for_project
277 def test_roles_for_project
272 # user with a role
278 # user with a role
273 roles = @jsmith.roles_for_project(Project.find(1))
279 roles = @jsmith.roles_for_project(Project.find(1))
274 assert_kind_of Role, roles.first
280 assert_kind_of Role, roles.first
275 assert_equal "Manager", roles.first.name
281 assert_equal "Manager", roles.first.name
276
282
277 # user with no role
283 # user with no role
278 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
284 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
279 end
285 end
280
286
281 def test_mail_notification_all
287 def test_mail_notification_all
282 @jsmith.mail_notification = true
288 @jsmith.mail_notification = true
283 @jsmith.notified_project_ids = []
289 @jsmith.notified_project_ids = []
284 @jsmith.save
290 @jsmith.save
285 @jsmith.reload
291 @jsmith.reload
286 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
292 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
287 end
293 end
288
294
289 def test_mail_notification_selected
295 def test_mail_notification_selected
290 @jsmith.mail_notification = false
296 @jsmith.mail_notification = false
291 @jsmith.notified_project_ids = [1]
297 @jsmith.notified_project_ids = [1]
292 @jsmith.save
298 @jsmith.save
293 @jsmith.reload
299 @jsmith.reload
294 assert Project.find(1).recipients.include?(@jsmith.mail)
300 assert Project.find(1).recipients.include?(@jsmith.mail)
295 end
301 end
296
302
297 def test_mail_notification_none
303 def test_mail_notification_none
298 @jsmith.mail_notification = false
304 @jsmith.mail_notification = false
299 @jsmith.notified_project_ids = []
305 @jsmith.notified_project_ids = []
300 @jsmith.save
306 @jsmith.save
301 @jsmith.reload
307 @jsmith.reload
302 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
308 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
303 end
309 end
304
310
305 def test_comments_sorting_preference
311 def test_comments_sorting_preference
306 assert !@jsmith.wants_comments_in_reverse_order?
312 assert !@jsmith.wants_comments_in_reverse_order?
307 @jsmith.pref.comments_sorting = 'asc'
313 @jsmith.pref.comments_sorting = 'asc'
308 assert !@jsmith.wants_comments_in_reverse_order?
314 assert !@jsmith.wants_comments_in_reverse_order?
309 @jsmith.pref.comments_sorting = 'desc'
315 @jsmith.pref.comments_sorting = 'desc'
310 assert @jsmith.wants_comments_in_reverse_order?
316 assert @jsmith.wants_comments_in_reverse_order?
311 end
317 end
312
318
313 def test_find_by_mail_should_be_case_insensitive
319 def test_find_by_mail_should_be_case_insensitive
314 u = User.find_by_mail('JSmith@somenet.foo')
320 u = User.find_by_mail('JSmith@somenet.foo')
315 assert_not_nil u
321 assert_not_nil u
316 assert_equal 'jsmith@somenet.foo', u.mail
322 assert_equal 'jsmith@somenet.foo', u.mail
317 end
323 end
318
324
319 def test_random_password
325 def test_random_password
320 u = User.new
326 u = User.new
321 u.random_password
327 u.random_password
322 assert !u.password.blank?
328 assert !u.password.blank?
323 assert !u.password_confirmation.blank?
329 assert !u.password_confirmation.blank?
324 end
330 end
325
331
326 context "#change_password_allowed?" do
332 context "#change_password_allowed?" do
327 should "be allowed if no auth source is set" do
333 should "be allowed if no auth source is set" do
328 user = User.generate_with_protected!
334 user = User.generate_with_protected!
329 assert user.change_password_allowed?
335 assert user.change_password_allowed?
330 end
336 end
331
337
332 should "delegate to the auth source" do
338 should "delegate to the auth source" do
333 user = User.generate_with_protected!
339 user = User.generate_with_protected!
334
340
335 allowed_auth_source = AuthSource.generate!
341 allowed_auth_source = AuthSource.generate!
336 def allowed_auth_source.allow_password_changes?; true; end
342 def allowed_auth_source.allow_password_changes?; true; end
337
343
338 denied_auth_source = AuthSource.generate!
344 denied_auth_source = AuthSource.generate!
339 def denied_auth_source.allow_password_changes?; false; end
345 def denied_auth_source.allow_password_changes?; false; end
340
346
341 assert user.change_password_allowed?
347 assert user.change_password_allowed?
342
348
343 user.auth_source = allowed_auth_source
349 user.auth_source = allowed_auth_source
344 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
350 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
345
351
346 user.auth_source = denied_auth_source
352 user.auth_source = denied_auth_source
347 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
353 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
348 end
354 end
349
355
350 end
356 end
351
357
352 if Object.const_defined?(:OpenID)
358 if Object.const_defined?(:OpenID)
353
359
354 def test_setting_identity_url
360 def test_setting_identity_url
355 normalized_open_id_url = 'http://example.com/'
361 normalized_open_id_url = 'http://example.com/'
356 u = User.new( :identity_url => 'http://example.com/' )
362 u = User.new( :identity_url => 'http://example.com/' )
357 assert_equal normalized_open_id_url, u.identity_url
363 assert_equal normalized_open_id_url, u.identity_url
358 end
364 end
359
365
360 def test_setting_identity_url_without_trailing_slash
366 def test_setting_identity_url_without_trailing_slash
361 normalized_open_id_url = 'http://example.com/'
367 normalized_open_id_url = 'http://example.com/'
362 u = User.new( :identity_url => 'http://example.com' )
368 u = User.new( :identity_url => 'http://example.com' )
363 assert_equal normalized_open_id_url, u.identity_url
369 assert_equal normalized_open_id_url, u.identity_url
364 end
370 end
365
371
366 def test_setting_identity_url_without_protocol
372 def test_setting_identity_url_without_protocol
367 normalized_open_id_url = 'http://example.com/'
373 normalized_open_id_url = 'http://example.com/'
368 u = User.new( :identity_url => 'example.com' )
374 u = User.new( :identity_url => 'example.com' )
369 assert_equal normalized_open_id_url, u.identity_url
375 assert_equal normalized_open_id_url, u.identity_url
370 end
376 end
371
377
372 def test_setting_blank_identity_url
378 def test_setting_blank_identity_url
373 u = User.new( :identity_url => 'example.com' )
379 u = User.new( :identity_url => 'example.com' )
374 u.identity_url = ''
380 u.identity_url = ''
375 assert u.identity_url.blank?
381 assert u.identity_url.blank?
376 end
382 end
377
383
378 def test_setting_invalid_identity_url
384 def test_setting_invalid_identity_url
379 u = User.new( :identity_url => 'this is not an openid url' )
385 u = User.new( :identity_url => 'this is not an openid url' )
380 assert u.identity_url.blank?
386 assert u.identity_url.blank?
381 end
387 end
382
388
383 else
389 else
384 puts "Skipping openid tests."
390 puts "Skipping openid tests."
385 end
391 end
386
392
387 end
393 end
General Comments 0
You need to be logged in to leave comments. Login now