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