##// END OF EJS Templates
Fixed: no email sent with 'Notifiy for any event on the selected projects only' (#7421)....
Jean-Philippe Lang -
r4641:73a2d926f6e4
parent child
Show More
@@ -1,537 +1,542
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < Principal
20 class User < Principal
21 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 USER_FORMATS = {
29 USER_FORMATS = {
30 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname_lastname => '#{firstname} #{lastname}',
31 :firstname => '#{firstname}',
31 :firstname => '#{firstname}',
32 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_firstname => '#{lastname} #{firstname}',
33 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :lastname_coma_firstname => '#{lastname}, #{firstname}',
34 :username => '#{login}'
34 :username => '#{login}'
35 }
35 }
36
36
37 MAIL_NOTIFICATION_OPTIONS = [
37 MAIL_NOTIFICATION_OPTIONS = [
38 ['all', :label_user_mail_option_all],
38 ['all', :label_user_mail_option_all],
39 ['selected', :label_user_mail_option_selected],
39 ['selected', :label_user_mail_option_selected],
40 ['only_my_events', :label_user_mail_option_only_my_events],
40 ['only_my_events', :label_user_mail_option_only_my_events],
41 ['only_assigned', :label_user_mail_option_only_assigned],
41 ['only_assigned', :label_user_mail_option_only_assigned],
42 ['only_owner', :label_user_mail_option_only_owner],
42 ['only_owner', :label_user_mail_option_only_owner],
43 ['none', :label_user_mail_option_none]
43 ['none', :label_user_mail_option_none]
44 ]
44 ]
45
45
46 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
46 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
47 :after_remove => Proc.new {|user, group| group.user_removed(user)}
47 :after_remove => Proc.new {|user, group| group.user_removed(user)}
48 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
48 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
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
57
58 acts_as_customizable
58 acts_as_customizable
59
59
60 attr_accessor :password, :password_confirmation
60 attr_accessor :password, :password_confirmation
61 attr_accessor :last_before_login_on
61 attr_accessor :last_before_login_on
62 # Prevents unauthorized assignments
62 # Prevents unauthorized assignments
63 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
63 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
64
64
65 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
65 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
66 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
66 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
67 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
67 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
68 # Login must contain lettres, numbers, underscores only
68 # Login must contain lettres, numbers, underscores only
69 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
69 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
70 validates_length_of :login, :maximum => 30
70 validates_length_of :login, :maximum => 30
71 validates_length_of :firstname, :lastname, :maximum => 30
71 validates_length_of :firstname, :lastname, :maximum => 30
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
75 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
75 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
76
76
77 before_destroy :remove_references_before_destroy
77 before_destroy :remove_references_before_destroy
78
78
79 def before_create
79 def before_create
80 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
80 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
81 true
81 true
82 end
82 end
83
83
84 def before_save
84 def before_save
85 # update hashed_password if password was set
85 # update hashed_password if password was set
86 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
86 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
87 end
87 end
88
88
89 def reload(*args)
89 def reload(*args)
90 @name = nil
90 @name = nil
91 super
91 super
92 end
92 end
93
93
94 def mail=(arg)
94 def mail=(arg)
95 write_attribute(:mail, arg.to_s.strip)
95 write_attribute(:mail, arg.to_s.strip)
96 end
96 end
97
97
98 def identity_url=(url)
98 def identity_url=(url)
99 if url.blank?
99 if url.blank?
100 write_attribute(:identity_url, '')
100 write_attribute(:identity_url, '')
101 else
101 else
102 begin
102 begin
103 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
103 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
104 rescue OpenIdAuthentication::InvalidOpenId
104 rescue OpenIdAuthentication::InvalidOpenId
105 # Invlaid url, don't save
105 # Invlaid url, don't save
106 end
106 end
107 end
107 end
108 self.read_attribute(:identity_url)
108 self.read_attribute(:identity_url)
109 end
109 end
110
110
111 # Returns the user that matches provided login and password, or nil
111 # Returns the user that matches provided login and password, or nil
112 def self.try_to_login(login, password)
112 def self.try_to_login(login, password)
113 # Make sure no one can sign in with an empty password
113 # Make sure no one can sign in with an empty password
114 return nil if password.to_s.empty?
114 return nil if password.to_s.empty?
115 user = find_by_login(login)
115 user = find_by_login(login)
116 if user
116 if user
117 # user is already in local database
117 # user is already in local database
118 return nil if !user.active?
118 return nil if !user.active?
119 if user.auth_source
119 if user.auth_source
120 # user has an external authentication method
120 # user has an external authentication method
121 return nil unless user.auth_source.authenticate(login, password)
121 return nil unless user.auth_source.authenticate(login, password)
122 else
122 else
123 # authentication with local password
123 # authentication with local password
124 return nil unless User.hash_password(password) == user.hashed_password
124 return nil unless User.hash_password(password) == user.hashed_password
125 end
125 end
126 else
126 else
127 # user is not yet registered, try to authenticate with available sources
127 # user is not yet registered, try to authenticate with available sources
128 attrs = AuthSource.authenticate(login, password)
128 attrs = AuthSource.authenticate(login, password)
129 if attrs
129 if attrs
130 user = new(attrs)
130 user = new(attrs)
131 user.login = login
131 user.login = login
132 user.language = Setting.default_language
132 user.language = Setting.default_language
133 if user.save
133 if user.save
134 user.reload
134 user.reload
135 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
135 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
136 end
136 end
137 end
137 end
138 end
138 end
139 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
139 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
140 user
140 user
141 rescue => text
141 rescue => text
142 raise text
142 raise text
143 end
143 end
144
144
145 # Returns the user who matches the given autologin +key+ or nil
145 # Returns the user who matches the given autologin +key+ or nil
146 def self.try_to_autologin(key)
146 def self.try_to_autologin(key)
147 tokens = Token.find_all_by_action_and_value('autologin', key)
147 tokens = Token.find_all_by_action_and_value('autologin', key)
148 # Make sure there's only 1 token that matches the key
148 # Make sure there's only 1 token that matches the key
149 if tokens.size == 1
149 if tokens.size == 1
150 token = tokens.first
150 token = tokens.first
151 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
151 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
152 token.user.update_attribute(:last_login_on, Time.now)
152 token.user.update_attribute(:last_login_on, Time.now)
153 token.user
153 token.user
154 end
154 end
155 end
155 end
156 end
156 end
157
157
158 # Return user's full name for display
158 # Return user's full name for display
159 def name(formatter = nil)
159 def name(formatter = nil)
160 if formatter
160 if formatter
161 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
161 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
162 else
162 else
163 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
163 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
164 end
164 end
165 end
165 end
166
166
167 def active?
167 def active?
168 self.status == STATUS_ACTIVE
168 self.status == STATUS_ACTIVE
169 end
169 end
170
170
171 def registered?
171 def registered?
172 self.status == STATUS_REGISTERED
172 self.status == STATUS_REGISTERED
173 end
173 end
174
174
175 def locked?
175 def locked?
176 self.status == STATUS_LOCKED
176 self.status == STATUS_LOCKED
177 end
177 end
178
178
179 def activate
179 def activate
180 self.status = STATUS_ACTIVE
180 self.status = STATUS_ACTIVE
181 end
181 end
182
182
183 def register
183 def register
184 self.status = STATUS_REGISTERED
184 self.status = STATUS_REGISTERED
185 end
185 end
186
186
187 def lock
187 def lock
188 self.status = STATUS_LOCKED
188 self.status = STATUS_LOCKED
189 end
189 end
190
190
191 def activate!
191 def activate!
192 update_attribute(:status, STATUS_ACTIVE)
192 update_attribute(:status, STATUS_ACTIVE)
193 end
193 end
194
194
195 def register!
195 def register!
196 update_attribute(:status, STATUS_REGISTERED)
196 update_attribute(:status, STATUS_REGISTERED)
197 end
197 end
198
198
199 def lock!
199 def lock!
200 update_attribute(:status, STATUS_LOCKED)
200 update_attribute(:status, STATUS_LOCKED)
201 end
201 end
202
202
203 def check_password?(clear_password)
203 def check_password?(clear_password)
204 if auth_source_id.present?
204 if auth_source_id.present?
205 auth_source.authenticate(self.login, clear_password)
205 auth_source.authenticate(self.login, clear_password)
206 else
206 else
207 User.hash_password(clear_password) == self.hashed_password
207 User.hash_password(clear_password) == self.hashed_password
208 end
208 end
209 end
209 end
210
210
211 # Does the backend storage allow this user to change their password?
211 # Does the backend storage allow this user to change their password?
212 def change_password_allowed?
212 def change_password_allowed?
213 return true if auth_source_id.blank?
213 return true if auth_source_id.blank?
214 return auth_source.allow_password_changes?
214 return auth_source.allow_password_changes?
215 end
215 end
216
216
217 # Generate and set a random password. Useful for automated user creation
217 # Generate and set a random password. Useful for automated user creation
218 # Based on Token#generate_token_value
218 # Based on Token#generate_token_value
219 #
219 #
220 def random_password
220 def random_password
221 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
221 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
222 password = ''
222 password = ''
223 40.times { |i| password << chars[rand(chars.size-1)] }
223 40.times { |i| password << chars[rand(chars.size-1)] }
224 self.password = password
224 self.password = password
225 self.password_confirmation = password
225 self.password_confirmation = password
226 self
226 self
227 end
227 end
228
228
229 def pref
229 def pref
230 self.preference ||= UserPreference.new(:user => self)
230 self.preference ||= UserPreference.new(:user => self)
231 end
231 end
232
232
233 def time_zone
233 def time_zone
234 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
234 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
235 end
235 end
236
236
237 def wants_comments_in_reverse_order?
237 def wants_comments_in_reverse_order?
238 self.pref[:comments_sorting] == 'desc'
238 self.pref[:comments_sorting] == 'desc'
239 end
239 end
240
240
241 # Return user's RSS key (a 40 chars long string), used to access feeds
241 # Return user's RSS key (a 40 chars long string), used to access feeds
242 def rss_key
242 def rss_key
243 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
243 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
244 token.value
244 token.value
245 end
245 end
246
246
247 # Return user's API key (a 40 chars long string), used to access the API
247 # Return user's API key (a 40 chars long string), used to access the API
248 def api_key
248 def api_key
249 token = self.api_token || self.create_api_token(:action => 'api')
249 token = self.api_token || self.create_api_token(:action => 'api')
250 token.value
250 token.value
251 end
251 end
252
252
253 # Return an array of project ids for which the user has explicitly turned mail notifications on
253 # Return an array of project ids for which the user has explicitly turned mail notifications on
254 def notified_projects_ids
254 def notified_projects_ids
255 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
255 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
256 end
256 end
257
257
258 def notified_project_ids=(ids)
258 def notified_project_ids=(ids)
259 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
259 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
260 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
260 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
261 @notified_projects_ids = nil
261 @notified_projects_ids = nil
262 notified_projects_ids
262 notified_projects_ids
263 end
263 end
264
264
265 def valid_notification_options
265 def valid_notification_options
266 self.class.valid_notification_options(self)
266 self.class.valid_notification_options(self)
267 end
267 end
268
268
269 # Only users that belong to more than 1 project can select projects for which they are notified
269 # Only users that belong to more than 1 project can select projects for which they are notified
270 def self.valid_notification_options(user=nil)
270 def self.valid_notification_options(user=nil)
271 # Note that @user.membership.size would fail since AR ignores
271 # Note that @user.membership.size would fail since AR ignores
272 # :include association option when doing a count
272 # :include association option when doing a count
273 if user.nil? || user.memberships.length < 1
273 if user.nil? || user.memberships.length < 1
274 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
274 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
275 else
275 else
276 MAIL_NOTIFICATION_OPTIONS
276 MAIL_NOTIFICATION_OPTIONS
277 end
277 end
278 end
278 end
279
279
280 # Find a user account by matching the exact login and then a case-insensitive
280 # Find a user account by matching the exact login and then a case-insensitive
281 # version. Exact matches will be given priority.
281 # version. Exact matches will be given priority.
282 def self.find_by_login(login)
282 def self.find_by_login(login)
283 # force string comparison to be case sensitive on MySQL
283 # force string comparison to be case sensitive on MySQL
284 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
284 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
285
285
286 # First look for an exact match
286 # First look for an exact match
287 user = first(:conditions => ["#{type_cast} login = ?", login])
287 user = first(:conditions => ["#{type_cast} login = ?", login])
288 # Fail over to case-insensitive if none was found
288 # Fail over to case-insensitive if none was found
289 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
289 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
290 end
290 end
291
291
292 def self.find_by_rss_key(key)
292 def self.find_by_rss_key(key)
293 token = Token.find_by_value(key)
293 token = Token.find_by_value(key)
294 token && token.user.active? ? token.user : nil
294 token && token.user.active? ? token.user : nil
295 end
295 end
296
296
297 def self.find_by_api_key(key)
297 def self.find_by_api_key(key)
298 token = Token.find_by_action_and_value('api', key)
298 token = Token.find_by_action_and_value('api', key)
299 token && token.user.active? ? token.user : nil
299 token && token.user.active? ? token.user : nil
300 end
300 end
301
301
302 # Makes find_by_mail case-insensitive
302 # Makes find_by_mail case-insensitive
303 def self.find_by_mail(mail)
303 def self.find_by_mail(mail)
304 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
304 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
305 end
305 end
306
306
307 def to_s
307 def to_s
308 name
308 name
309 end
309 end
310
310
311 # Returns the current day according to user's time zone
311 # Returns the current day according to user's time zone
312 def today
312 def today
313 if time_zone.nil?
313 if time_zone.nil?
314 Date.today
314 Date.today
315 else
315 else
316 Time.now.in_time_zone(time_zone).to_date
316 Time.now.in_time_zone(time_zone).to_date
317 end
317 end
318 end
318 end
319
319
320 def logged?
320 def logged?
321 true
321 true
322 end
322 end
323
323
324 def anonymous?
324 def anonymous?
325 !logged?
325 !logged?
326 end
326 end
327
327
328 # Return user's roles for project
328 # Return user's roles for project
329 def roles_for_project(project)
329 def roles_for_project(project)
330 roles = []
330 roles = []
331 # No role on archived projects
331 # No role on archived projects
332 return roles unless project && project.active?
332 return roles unless project && project.active?
333 if logged?
333 if logged?
334 # Find project membership
334 # Find project membership
335 membership = memberships.detect {|m| m.project_id == project.id}
335 membership = memberships.detect {|m| m.project_id == project.id}
336 if membership
336 if membership
337 roles = membership.roles
337 roles = membership.roles
338 else
338 else
339 @role_non_member ||= Role.non_member
339 @role_non_member ||= Role.non_member
340 roles << @role_non_member
340 roles << @role_non_member
341 end
341 end
342 else
342 else
343 @role_anonymous ||= Role.anonymous
343 @role_anonymous ||= Role.anonymous
344 roles << @role_anonymous
344 roles << @role_anonymous
345 end
345 end
346 roles
346 roles
347 end
347 end
348
348
349 # Return true if the user is a member of project
349 # Return true if the user is a member of project
350 def member_of?(project)
350 def member_of?(project)
351 !roles_for_project(project).detect {|role| role.member?}.nil?
351 !roles_for_project(project).detect {|role| role.member?}.nil?
352 end
352 end
353
353
354 # Return true if the user is allowed to do the specified action on a specific context
354 # Return true if the user is allowed to do the specified action on a specific context
355 # Action can be:
355 # Action can be:
356 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
356 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
357 # * a permission Symbol (eg. :edit_project)
357 # * a permission Symbol (eg. :edit_project)
358 # Context can be:
358 # Context can be:
359 # * a project : returns true if user is allowed to do the specified action on this project
359 # * a project : returns true if user is allowed to do the specified action on this project
360 # * a group of projects : returns true if user is allowed on every project
360 # * a group of projects : returns true if user is allowed on every project
361 # * nil with options[:global] set : check if user has at least one role allowed for this action,
361 # * nil with options[:global] set : check if user has at least one role allowed for this action,
362 # or falls back to Non Member / Anonymous permissions depending if the user is logged
362 # or falls back to Non Member / Anonymous permissions depending if the user is logged
363 def allowed_to?(action, context, options={})
363 def allowed_to?(action, context, options={})
364 if context && context.is_a?(Project)
364 if context && context.is_a?(Project)
365 # No action allowed on archived projects
365 # No action allowed on archived projects
366 return false unless context.active?
366 return false unless context.active?
367 # No action allowed on disabled modules
367 # No action allowed on disabled modules
368 return false unless context.allows_to?(action)
368 return false unless context.allows_to?(action)
369 # Admin users are authorized for anything else
369 # Admin users are authorized for anything else
370 return true if admin?
370 return true if admin?
371
371
372 roles = roles_for_project(context)
372 roles = roles_for_project(context)
373 return false unless roles
373 return false unless roles
374 roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
374 roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
375
375
376 elsif context && context.is_a?(Array)
376 elsif context && context.is_a?(Array)
377 # Authorize if user is authorized on every element of the array
377 # Authorize if user is authorized on every element of the array
378 context.map do |project|
378 context.map do |project|
379 allowed_to?(action,project,options)
379 allowed_to?(action,project,options)
380 end.inject do |memo,allowed|
380 end.inject do |memo,allowed|
381 memo && allowed
381 memo && allowed
382 end
382 end
383 elsif options[:global]
383 elsif options[:global]
384 # Admin users are always authorized
384 # Admin users are always authorized
385 return true if admin?
385 return true if admin?
386
386
387 # authorize if user has at least one role that has this permission
387 # authorize if user has at least one role that has this permission
388 roles = memberships.collect {|m| m.roles}.flatten.uniq
388 roles = memberships.collect {|m| m.roles}.flatten.uniq
389 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
389 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
390 else
390 else
391 false
391 false
392 end
392 end
393 end
393 end
394
394
395 # Is the user allowed to do the specified action on any project?
395 # Is the user allowed to do the specified action on any project?
396 # See allowed_to? for the actions and valid options.
396 # See allowed_to? for the actions and valid options.
397 def allowed_to_globally?(action, options)
397 def allowed_to_globally?(action, options)
398 allowed_to?(action, nil, options.reverse_merge(:global => true))
398 allowed_to?(action, nil, options.reverse_merge(:global => true))
399 end
399 end
400
400
401 safe_attributes 'login',
401 safe_attributes 'login',
402 'firstname',
402 'firstname',
403 'lastname',
403 'lastname',
404 'mail',
404 'mail',
405 'mail_notification',
405 'mail_notification',
406 'language',
406 'language',
407 'custom_field_values',
407 'custom_field_values',
408 'custom_fields',
408 'custom_fields',
409 'identity_url'
409 'identity_url'
410
410
411 safe_attributes 'status',
411 safe_attributes 'status',
412 'auth_source_id',
412 'auth_source_id',
413 :if => lambda {|user, current_user| current_user.admin?}
413 :if => lambda {|user, current_user| current_user.admin?}
414
414
415 safe_attributes 'group_ids',
415 safe_attributes 'group_ids',
416 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
416 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
417
417
418 # Utility method to help check if a user should be notified about an
418 # Utility method to help check if a user should be notified about an
419 # event.
419 # event.
420 #
420 #
421 # TODO: only supports Issue events currently
421 # TODO: only supports Issue events currently
422 def notify_about?(object)
422 def notify_about?(object)
423 case mail_notification
423 case mail_notification
424 when 'all'
424 when 'all'
425 true
425 true
426 when 'selected'
426 when 'selected'
427 # Handled by the Project
427 # user receives notifications for created/assigned issues on unselected projects
428 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
429 true
430 else
431 false
432 end
428 when 'none'
433 when 'none'
429 false
434 false
430 when 'only_my_events'
435 when 'only_my_events'
431 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
436 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
432 true
437 true
433 else
438 else
434 false
439 false
435 end
440 end
436 when 'only_assigned'
441 when 'only_assigned'
437 if object.is_a?(Issue) && object.assigned_to == self
442 if object.is_a?(Issue) && object.assigned_to == self
438 true
443 true
439 else
444 else
440 false
445 false
441 end
446 end
442 when 'only_owner'
447 when 'only_owner'
443 if object.is_a?(Issue) && object.author == self
448 if object.is_a?(Issue) && object.author == self
444 true
449 true
445 else
450 else
446 false
451 false
447 end
452 end
448 else
453 else
449 false
454 false
450 end
455 end
451 end
456 end
452
457
453 def self.current=(user)
458 def self.current=(user)
454 @current_user = user
459 @current_user = user
455 end
460 end
456
461
457 def self.current
462 def self.current
458 @current_user ||= User.anonymous
463 @current_user ||= User.anonymous
459 end
464 end
460
465
461 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
466 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
462 # one anonymous user per database.
467 # one anonymous user per database.
463 def self.anonymous
468 def self.anonymous
464 anonymous_user = AnonymousUser.find(:first)
469 anonymous_user = AnonymousUser.find(:first)
465 if anonymous_user.nil?
470 if anonymous_user.nil?
466 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
471 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
467 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
472 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
468 end
473 end
469 anonymous_user
474 anonymous_user
470 end
475 end
471
476
472 protected
477 protected
473
478
474 def validate
479 def validate
475 # Password length validation based on setting
480 # Password length validation based on setting
476 if !password.nil? && password.size < Setting.password_min_length.to_i
481 if !password.nil? && password.size < Setting.password_min_length.to_i
477 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
482 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
478 end
483 end
479 end
484 end
480
485
481 private
486 private
482
487
483 # Removes references that are not handled by associations
488 # Removes references that are not handled by associations
484 # Things that are not deleted are reassociated with the anonymous user
489 # Things that are not deleted are reassociated with the anonymous user
485 def remove_references_before_destroy
490 def remove_references_before_destroy
486 return if self.id.nil?
491 return if self.id.nil?
487
492
488 substitute = User.anonymous
493 substitute = User.anonymous
489 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
494 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
490 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
495 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
491 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
496 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
492 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
497 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
493 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
498 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
494 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
499 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
495 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
500 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
496 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
501 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
497 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
502 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
498 # Remove private queries and keep public ones
503 # Remove private queries and keep public ones
499 Query.delete_all ['user_id = ? AND is_public = ?', id, false]
504 Query.delete_all ['user_id = ? AND is_public = ?', id, false]
500 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
505 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
501 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
506 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
502 Token.delete_all ['user_id = ?', id]
507 Token.delete_all ['user_id = ?', id]
503 Watcher.delete_all ['user_id = ?', id]
508 Watcher.delete_all ['user_id = ?', id]
504 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
509 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
505 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
510 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
506 end
511 end
507
512
508 # Return password digest
513 # Return password digest
509 def self.hash_password(clear_password)
514 def self.hash_password(clear_password)
510 Digest::SHA1.hexdigest(clear_password || "")
515 Digest::SHA1.hexdigest(clear_password || "")
511 end
516 end
512 end
517 end
513
518
514 class AnonymousUser < User
519 class AnonymousUser < User
515
520
516 def validate_on_create
521 def validate_on_create
517 # There should be only one AnonymousUser in the database
522 # There should be only one AnonymousUser in the database
518 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
523 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
519 end
524 end
520
525
521 def available_custom_fields
526 def available_custom_fields
522 []
527 []
523 end
528 end
524
529
525 # Overrides a few properties
530 # Overrides a few properties
526 def logged?; false end
531 def logged?; false end
527 def admin; false end
532 def admin; false end
528 def name(*args); I18n.t(:label_user_anonymous) end
533 def name(*args); I18n.t(:label_user_anonymous) end
529 def mail; nil end
534 def mail; nil end
530 def time_zone; nil end
535 def time_zone; nil end
531 def rss_key; nil end
536 def rss_key; nil end
532
537
533 # Anonymous user can not be destroyed
538 # Anonymous user can not be destroyed
534 def destroy
539 def destroy
535 false
540 false
536 end
541 end
537 end
542 end
@@ -1,749 +1,766
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
22
23 def setup
23 def setup
24 @admin = User.find(1)
24 @admin = User.find(1)
25 @jsmith = User.find(2)
25 @jsmith = User.find(2)
26 @dlopper = User.find(3)
26 @dlopper = User.find(3)
27 end
27 end
28
28
29 test 'object_daddy creation' do
29 test 'object_daddy creation' do
30 User.generate_with_protected!(:firstname => 'Testing connection')
30 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
31 User.generate_with_protected!(:firstname => 'Testing connection')
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
33 end
33 end
34
34
35 def test_truth
35 def test_truth
36 assert_kind_of User, @jsmith
36 assert_kind_of User, @jsmith
37 end
37 end
38
38
39 def test_mail_should_be_stripped
39 def test_mail_should_be_stripped
40 u = User.new
40 u = User.new
41 u.mail = " foo@bar.com "
41 u.mail = " foo@bar.com "
42 assert_equal "foo@bar.com", u.mail
42 assert_equal "foo@bar.com", u.mail
43 end
43 end
44
44
45 def test_create
45 def test_create
46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
47
47
48 user.login = "jsmith"
48 user.login = "jsmith"
49 user.password, user.password_confirmation = "password", "password"
49 user.password, user.password_confirmation = "password", "password"
50 # login uniqueness
50 # login uniqueness
51 assert !user.save
51 assert !user.save
52 assert_equal 1, user.errors.count
52 assert_equal 1, user.errors.count
53
53
54 user.login = "newuser"
54 user.login = "newuser"
55 user.password, user.password_confirmation = "passwd", "password"
55 user.password, user.password_confirmation = "passwd", "password"
56 # password confirmation
56 # password confirmation
57 assert !user.save
57 assert !user.save
58 assert_equal 1, user.errors.count
58 assert_equal 1, user.errors.count
59
59
60 user.password, user.password_confirmation = "password", "password"
60 user.password, user.password_confirmation = "password", "password"
61 assert user.save
61 assert user.save
62 end
62 end
63
63
64 context "User#before_create" do
64 context "User#before_create" do
65 should "set the mail_notification to the default Setting" do
65 should "set the mail_notification to the default Setting" do
66 @user1 = User.generate_with_protected!
66 @user1 = User.generate_with_protected!
67 assert_equal 'only_my_events', @user1.mail_notification
67 assert_equal 'only_my_events', @user1.mail_notification
68
68
69 with_settings :default_notification_option => 'all' do
69 with_settings :default_notification_option => 'all' do
70 @user2 = User.generate_with_protected!
70 @user2 = User.generate_with_protected!
71 assert_equal 'all', @user2.mail_notification
71 assert_equal 'all', @user2.mail_notification
72 end
72 end
73 end
73 end
74 end
74 end
75
75
76 context "User.login" do
76 context "User.login" do
77 should "be case-insensitive." do
77 should "be case-insensitive." do
78 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
78 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
79 u.login = 'newuser'
79 u.login = 'newuser'
80 u.password, u.password_confirmation = "password", "password"
80 u.password, u.password_confirmation = "password", "password"
81 assert u.save
81 assert u.save
82
82
83 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
83 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
84 u.login = 'NewUser'
84 u.login = 'NewUser'
85 u.password, u.password_confirmation = "password", "password"
85 u.password, u.password_confirmation = "password", "password"
86 assert !u.save
86 assert !u.save
87 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
87 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
88 end
88 end
89 end
89 end
90
90
91 def test_mail_uniqueness_should_not_be_case_sensitive
91 def test_mail_uniqueness_should_not_be_case_sensitive
92 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
92 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
93 u.login = 'newuser1'
93 u.login = 'newuser1'
94 u.password, u.password_confirmation = "password", "password"
94 u.password, u.password_confirmation = "password", "password"
95 assert u.save
95 assert u.save
96
96
97 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
97 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
98 u.login = 'newuser2'
98 u.login = 'newuser2'
99 u.password, u.password_confirmation = "password", "password"
99 u.password, u.password_confirmation = "password", "password"
100 assert !u.save
100 assert !u.save
101 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
101 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
102 end
102 end
103
103
104 def test_update
104 def test_update
105 assert_equal "admin", @admin.login
105 assert_equal "admin", @admin.login
106 @admin.login = "john"
106 @admin.login = "john"
107 assert @admin.save, @admin.errors.full_messages.join("; ")
107 assert @admin.save, @admin.errors.full_messages.join("; ")
108 @admin.reload
108 @admin.reload
109 assert_equal "john", @admin.login
109 assert_equal "john", @admin.login
110 end
110 end
111
111
112 def test_destroy_should_delete_members_and_roles
112 def test_destroy_should_delete_members_and_roles
113 members = Member.find_all_by_user_id(2)
113 members = Member.find_all_by_user_id(2)
114 ms = members.size
114 ms = members.size
115 rs = members.collect(&:roles).flatten.size
115 rs = members.collect(&:roles).flatten.size
116
116
117 assert_difference 'Member.count', - ms do
117 assert_difference 'Member.count', - ms do
118 assert_difference 'MemberRole.count', - rs do
118 assert_difference 'MemberRole.count', - rs do
119 User.find(2).destroy
119 User.find(2).destroy
120 end
120 end
121 end
121 end
122
122
123 assert_nil User.find_by_id(2)
123 assert_nil User.find_by_id(2)
124 assert Member.find_all_by_user_id(2).empty?
124 assert Member.find_all_by_user_id(2).empty?
125 end
125 end
126
126
127 def test_destroy_should_update_attachments
127 def test_destroy_should_update_attachments
128 attachment = Attachment.create!(:container => Project.find(1),
128 attachment = Attachment.create!(:container => Project.find(1),
129 :file => uploaded_test_file("testfile.txt", "text/plain"),
129 :file => uploaded_test_file("testfile.txt", "text/plain"),
130 :author_id => 2)
130 :author_id => 2)
131
131
132 User.find(2).destroy
132 User.find(2).destroy
133 assert_nil User.find_by_id(2)
133 assert_nil User.find_by_id(2)
134 assert_equal User.anonymous, attachment.reload.author
134 assert_equal User.anonymous, attachment.reload.author
135 end
135 end
136
136
137 def test_destroy_should_update_comments
137 def test_destroy_should_update_comments
138 comment = Comment.create!(
138 comment = Comment.create!(
139 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
139 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
140 :author => User.find(2),
140 :author => User.find(2),
141 :comments => 'foo'
141 :comments => 'foo'
142 )
142 )
143
143
144 User.find(2).destroy
144 User.find(2).destroy
145 assert_nil User.find_by_id(2)
145 assert_nil User.find_by_id(2)
146 assert_equal User.anonymous, comment.reload.author
146 assert_equal User.anonymous, comment.reload.author
147 end
147 end
148
148
149 def test_destroy_should_update_issues
149 def test_destroy_should_update_issues
150 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
150 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
151
151
152 User.find(2).destroy
152 User.find(2).destroy
153 assert_nil User.find_by_id(2)
153 assert_nil User.find_by_id(2)
154 assert_equal User.anonymous, issue.reload.author
154 assert_equal User.anonymous, issue.reload.author
155 end
155 end
156
156
157 def test_destroy_should_unassign_issues
157 def test_destroy_should_unassign_issues
158 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
158 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
159
159
160 User.find(2).destroy
160 User.find(2).destroy
161 assert_nil User.find_by_id(2)
161 assert_nil User.find_by_id(2)
162 assert_nil issue.reload.assigned_to
162 assert_nil issue.reload.assigned_to
163 end
163 end
164
164
165 def test_destroy_should_update_journals
165 def test_destroy_should_update_journals
166 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
166 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
167 issue.init_journal(User.find(2), "update")
167 issue.init_journal(User.find(2), "update")
168 issue.save!
168 issue.save!
169
169
170 User.find(2).destroy
170 User.find(2).destroy
171 assert_nil User.find_by_id(2)
171 assert_nil User.find_by_id(2)
172 assert_equal User.anonymous, issue.journals.first.reload.user
172 assert_equal User.anonymous, issue.journals.first.reload.user
173 end
173 end
174
174
175 def test_destroy_should_update_journal_details_old_value
175 def test_destroy_should_update_journal_details_old_value
176 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
176 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
177 issue.init_journal(User.find(1), "update")
177 issue.init_journal(User.find(1), "update")
178 issue.assigned_to_id = nil
178 issue.assigned_to_id = nil
179 assert_difference 'JournalDetail.count' do
179 assert_difference 'JournalDetail.count' do
180 issue.save!
180 issue.save!
181 end
181 end
182 journal_detail = JournalDetail.first(:order => 'id DESC')
182 journal_detail = JournalDetail.first(:order => 'id DESC')
183 assert_equal '2', journal_detail.old_value
183 assert_equal '2', journal_detail.old_value
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_equal User.anonymous.id.to_s, journal_detail.reload.old_value
187 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
188 end
188 end
189
189
190 def test_destroy_should_update_journal_details_value
190 def test_destroy_should_update_journal_details_value
191 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
191 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
192 issue.init_journal(User.find(1), "update")
192 issue.init_journal(User.find(1), "update")
193 issue.assigned_to_id = 2
193 issue.assigned_to_id = 2
194 assert_difference 'JournalDetail.count' do
194 assert_difference 'JournalDetail.count' do
195 issue.save!
195 issue.save!
196 end
196 end
197 journal_detail = JournalDetail.first(:order => 'id DESC')
197 journal_detail = JournalDetail.first(:order => 'id DESC')
198 assert_equal '2', journal_detail.value
198 assert_equal '2', journal_detail.value
199
199
200 User.find(2).destroy
200 User.find(2).destroy
201 assert_nil User.find_by_id(2)
201 assert_nil User.find_by_id(2)
202 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
202 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
203 end
203 end
204
204
205 def test_destroy_should_update_messages
205 def test_destroy_should_update_messages
206 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
206 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
207 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
207 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
208
208
209 User.find(2).destroy
209 User.find(2).destroy
210 assert_nil User.find_by_id(2)
210 assert_nil User.find_by_id(2)
211 assert_equal User.anonymous, message.reload.author
211 assert_equal User.anonymous, message.reload.author
212 end
212 end
213
213
214 def test_destroy_should_update_news
214 def test_destroy_should_update_news
215 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
215 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
216
216
217 User.find(2).destroy
217 User.find(2).destroy
218 assert_nil User.find_by_id(2)
218 assert_nil User.find_by_id(2)
219 assert_equal User.anonymous, news.reload.author
219 assert_equal User.anonymous, news.reload.author
220 end
220 end
221
221
222 def test_destroy_should_delete_private_queries
222 def test_destroy_should_delete_private_queries
223 query = Query.new(:name => 'foo', :is_public => false)
223 query = Query.new(:name => 'foo', :is_public => false)
224 query.project_id = 1
224 query.project_id = 1
225 query.user_id = 2
225 query.user_id = 2
226 query.save!
226 query.save!
227
227
228 User.find(2).destroy
228 User.find(2).destroy
229 assert_nil User.find_by_id(2)
229 assert_nil User.find_by_id(2)
230 assert_nil Query.find_by_id(query.id)
230 assert_nil Query.find_by_id(query.id)
231 end
231 end
232
232
233 def test_destroy_should_update_public_queries
233 def test_destroy_should_update_public_queries
234 query = Query.new(:name => 'foo', :is_public => true)
234 query = Query.new(:name => 'foo', :is_public => true)
235 query.project_id = 1
235 query.project_id = 1
236 query.user_id = 2
236 query.user_id = 2
237 query.save!
237 query.save!
238
238
239 User.find(2).destroy
239 User.find(2).destroy
240 assert_nil User.find_by_id(2)
240 assert_nil User.find_by_id(2)
241 assert_equal User.anonymous, query.reload.user
241 assert_equal User.anonymous, query.reload.user
242 end
242 end
243
243
244 def test_destroy_should_update_time_entries
244 def test_destroy_should_update_time_entries
245 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
245 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
246 entry.project_id = 1
246 entry.project_id = 1
247 entry.user_id = 2
247 entry.user_id = 2
248 entry.save!
248 entry.save!
249
249
250 User.find(2).destroy
250 User.find(2).destroy
251 assert_nil User.find_by_id(2)
251 assert_nil User.find_by_id(2)
252 assert_equal User.anonymous, entry.reload.user
252 assert_equal User.anonymous, entry.reload.user
253 end
253 end
254
254
255 def test_destroy_should_delete_tokens
255 def test_destroy_should_delete_tokens
256 token = Token.create!(:user_id => 2, :value => 'foo')
256 token = Token.create!(:user_id => 2, :value => 'foo')
257
257
258 User.find(2).destroy
258 User.find(2).destroy
259 assert_nil User.find_by_id(2)
259 assert_nil User.find_by_id(2)
260 assert_nil Token.find_by_id(token.id)
260 assert_nil Token.find_by_id(token.id)
261 end
261 end
262
262
263 def test_destroy_should_delete_watchers
263 def test_destroy_should_delete_watchers
264 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
264 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
265 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
265 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
266
266
267 User.find(2).destroy
267 User.find(2).destroy
268 assert_nil User.find_by_id(2)
268 assert_nil User.find_by_id(2)
269 assert_nil Watcher.find_by_id(watcher.id)
269 assert_nil Watcher.find_by_id(watcher.id)
270 end
270 end
271
271
272 def test_destroy_should_update_wiki_contents
272 def test_destroy_should_update_wiki_contents
273 wiki_content = WikiContent.create!(
273 wiki_content = WikiContent.create!(
274 :text => 'foo',
274 :text => 'foo',
275 :author_id => 2,
275 :author_id => 2,
276 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
276 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
277 )
277 )
278 wiki_content.text = 'bar'
278 wiki_content.text = 'bar'
279 assert_difference 'WikiContent::Version.count' do
279 assert_difference 'WikiContent::Version.count' do
280 wiki_content.save!
280 wiki_content.save!
281 end
281 end
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_equal User.anonymous, wiki_content.reload.author
285 assert_equal User.anonymous, wiki_content.reload.author
286 wiki_content.versions.each do |version|
286 wiki_content.versions.each do |version|
287 assert_equal User.anonymous, version.reload.author
287 assert_equal User.anonymous, version.reload.author
288 end
288 end
289 end
289 end
290
290
291 def test_destroy_should_nullify_issue_categories
291 def test_destroy_should_nullify_issue_categories
292 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
292 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
293
293
294 User.find(2).destroy
294 User.find(2).destroy
295 assert_nil User.find_by_id(2)
295 assert_nil User.find_by_id(2)
296 assert_nil category.reload.assigned_to_id
296 assert_nil category.reload.assigned_to_id
297 end
297 end
298
298
299 def test_destroy_should_nullify_changesets
299 def test_destroy_should_nullify_changesets
300 changeset = Changeset.create!(
300 changeset = Changeset.create!(
301 :repository => Repository::Subversion.create!(
301 :repository => Repository::Subversion.create!(
302 :project_id => 1,
302 :project_id => 1,
303 :url => 'file:///var/svn'
303 :url => 'file:///var/svn'
304 ),
304 ),
305 :revision => '12',
305 :revision => '12',
306 :committed_on => Time.now,
306 :committed_on => Time.now,
307 :committer => 'jsmith'
307 :committer => 'jsmith'
308 )
308 )
309 assert_equal 2, changeset.user_id
309 assert_equal 2, changeset.user_id
310
310
311 User.find(2).destroy
311 User.find(2).destroy
312 assert_nil User.find_by_id(2)
312 assert_nil User.find_by_id(2)
313 assert_nil changeset.reload.user_id
313 assert_nil changeset.reload.user_id
314 end
314 end
315
315
316 def test_anonymous_user_should_not_be_destroyable
316 def test_anonymous_user_should_not_be_destroyable
317 assert_no_difference 'User.count' do
317 assert_no_difference 'User.count' do
318 assert_equal false, User.anonymous.destroy
318 assert_equal false, User.anonymous.destroy
319 end
319 end
320 end
320 end
321
321
322 def test_validate_login_presence
322 def test_validate_login_presence
323 @admin.login = ""
323 @admin.login = ""
324 assert !@admin.save
324 assert !@admin.save
325 assert_equal 1, @admin.errors.count
325 assert_equal 1, @admin.errors.count
326 end
326 end
327
327
328 def test_validate_mail_notification_inclusion
328 def test_validate_mail_notification_inclusion
329 u = User.new
329 u = User.new
330 u.mail_notification = 'foo'
330 u.mail_notification = 'foo'
331 u.save
331 u.save
332 assert_not_nil u.errors.on(:mail_notification)
332 assert_not_nil u.errors.on(:mail_notification)
333 end
333 end
334
334
335 context "User#try_to_login" do
335 context "User#try_to_login" do
336 should "fall-back to case-insensitive if user login is not found as-typed." do
336 should "fall-back to case-insensitive if user login is not found as-typed." do
337 user = User.try_to_login("AdMin", "admin")
337 user = User.try_to_login("AdMin", "admin")
338 assert_kind_of User, user
338 assert_kind_of User, user
339 assert_equal "admin", user.login
339 assert_equal "admin", user.login
340 end
340 end
341
341
342 should "select the exact matching user first" do
342 should "select the exact matching user first" do
343 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
343 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
344 # bypass validations to make it appear like existing data
344 # bypass validations to make it appear like existing data
345 case_sensitive_user.update_attribute(:login, 'ADMIN')
345 case_sensitive_user.update_attribute(:login, 'ADMIN')
346
346
347 user = User.try_to_login("ADMIN", "admin")
347 user = User.try_to_login("ADMIN", "admin")
348 assert_kind_of User, user
348 assert_kind_of User, user
349 assert_equal "ADMIN", user.login
349 assert_equal "ADMIN", user.login
350
350
351 end
351 end
352 end
352 end
353
353
354 def test_password
354 def test_password
355 user = User.try_to_login("admin", "admin")
355 user = User.try_to_login("admin", "admin")
356 assert_kind_of User, user
356 assert_kind_of User, user
357 assert_equal "admin", user.login
357 assert_equal "admin", user.login
358 user.password = "hello"
358 user.password = "hello"
359 assert user.save
359 assert user.save
360
360
361 user = User.try_to_login("admin", "hello")
361 user = User.try_to_login("admin", "hello")
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 assert_equal User.hash_password("hello"), user.hashed_password
364 assert_equal User.hash_password("hello"), user.hashed_password
365 end
365 end
366
366
367 def test_name_format
367 def test_name_format
368 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
368 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
369 Setting.user_format = :firstname_lastname
369 Setting.user_format = :firstname_lastname
370 assert_equal 'John Smith', @jsmith.reload.name
370 assert_equal 'John Smith', @jsmith.reload.name
371 Setting.user_format = :username
371 Setting.user_format = :username
372 assert_equal 'jsmith', @jsmith.reload.name
372 assert_equal 'jsmith', @jsmith.reload.name
373 end
373 end
374
374
375 def test_lock
375 def test_lock
376 user = User.try_to_login("jsmith", "jsmith")
376 user = User.try_to_login("jsmith", "jsmith")
377 assert_equal @jsmith, user
377 assert_equal @jsmith, user
378
378
379 @jsmith.status = User::STATUS_LOCKED
379 @jsmith.status = User::STATUS_LOCKED
380 assert @jsmith.save
380 assert @jsmith.save
381
381
382 user = User.try_to_login("jsmith", "jsmith")
382 user = User.try_to_login("jsmith", "jsmith")
383 assert_equal nil, user
383 assert_equal nil, user
384 end
384 end
385
385
386 if ldap_configured?
386 if ldap_configured?
387 context "#try_to_login using LDAP" do
387 context "#try_to_login using LDAP" do
388 context "with failed connection to the LDAP server" do
388 context "with failed connection to the LDAP server" do
389 should "return nil" do
389 should "return nil" do
390 @auth_source = AuthSourceLdap.find(1)
390 @auth_source = AuthSourceLdap.find(1)
391 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
391 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
392
392
393 assert_equal nil, User.try_to_login('edavis', 'wrong')
393 assert_equal nil, User.try_to_login('edavis', 'wrong')
394 end
394 end
395 end
395 end
396
396
397 context "with an unsuccessful authentication" do
397 context "with an unsuccessful authentication" do
398 should "return nil" do
398 should "return nil" do
399 assert_equal nil, User.try_to_login('edavis', 'wrong')
399 assert_equal nil, User.try_to_login('edavis', 'wrong')
400 end
400 end
401 end
401 end
402
402
403 context "on the fly registration" do
403 context "on the fly registration" do
404 setup do
404 setup do
405 @auth_source = AuthSourceLdap.find(1)
405 @auth_source = AuthSourceLdap.find(1)
406 end
406 end
407
407
408 context "with a successful authentication" do
408 context "with a successful authentication" do
409 should "create a new user account if it doesn't exist" do
409 should "create a new user account if it doesn't exist" do
410 assert_difference('User.count') do
410 assert_difference('User.count') do
411 user = User.try_to_login('edavis', '123456')
411 user = User.try_to_login('edavis', '123456')
412 assert !user.admin?
412 assert !user.admin?
413 end
413 end
414 end
414 end
415
415
416 should "retrieve existing user" do
416 should "retrieve existing user" do
417 user = User.try_to_login('edavis', '123456')
417 user = User.try_to_login('edavis', '123456')
418 user.admin = true
418 user.admin = true
419 user.save!
419 user.save!
420
420
421 assert_no_difference('User.count') do
421 assert_no_difference('User.count') do
422 user = User.try_to_login('edavis', '123456')
422 user = User.try_to_login('edavis', '123456')
423 assert user.admin?
423 assert user.admin?
424 end
424 end
425 end
425 end
426 end
426 end
427 end
427 end
428 end
428 end
429
429
430 else
430 else
431 puts "Skipping LDAP tests."
431 puts "Skipping LDAP tests."
432 end
432 end
433
433
434 def test_create_anonymous
434 def test_create_anonymous
435 AnonymousUser.delete_all
435 AnonymousUser.delete_all
436 anon = User.anonymous
436 anon = User.anonymous
437 assert !anon.new_record?
437 assert !anon.new_record?
438 assert_kind_of AnonymousUser, anon
438 assert_kind_of AnonymousUser, anon
439 end
439 end
440
440
441 should_have_one :rss_token
441 should_have_one :rss_token
442
442
443 def test_rss_key
443 def test_rss_key
444 assert_nil @jsmith.rss_token
444 assert_nil @jsmith.rss_token
445 key = @jsmith.rss_key
445 key = @jsmith.rss_key
446 assert_equal 40, key.length
446 assert_equal 40, key.length
447
447
448 @jsmith.reload
448 @jsmith.reload
449 assert_equal key, @jsmith.rss_key
449 assert_equal key, @jsmith.rss_key
450 end
450 end
451
451
452
452
453 should_have_one :api_token
453 should_have_one :api_token
454
454
455 context "User#api_key" do
455 context "User#api_key" do
456 should "generate a new one if the user doesn't have one" do
456 should "generate a new one if the user doesn't have one" do
457 user = User.generate_with_protected!(:api_token => nil)
457 user = User.generate_with_protected!(:api_token => nil)
458 assert_nil user.api_token
458 assert_nil user.api_token
459
459
460 key = user.api_key
460 key = user.api_key
461 assert_equal 40, key.length
461 assert_equal 40, key.length
462 user.reload
462 user.reload
463 assert_equal key, user.api_key
463 assert_equal key, user.api_key
464 end
464 end
465
465
466 should "return the existing api token value" do
466 should "return the existing api token value" do
467 user = User.generate_with_protected!
467 user = User.generate_with_protected!
468 token = Token.generate!(:action => 'api')
468 token = Token.generate!(:action => 'api')
469 user.api_token = token
469 user.api_token = token
470 assert user.save
470 assert user.save
471
471
472 assert_equal token.value, user.api_key
472 assert_equal token.value, user.api_key
473 end
473 end
474 end
474 end
475
475
476 context "User#find_by_api_key" do
476 context "User#find_by_api_key" do
477 should "return nil if no matching key is found" do
477 should "return nil if no matching key is found" do
478 assert_nil User.find_by_api_key('zzzzzzzzz')
478 assert_nil User.find_by_api_key('zzzzzzzzz')
479 end
479 end
480
480
481 should "return nil if the key is found for an inactive user" do
481 should "return nil if the key is found for an inactive user" do
482 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
482 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
483 token = Token.generate!(:action => 'api')
483 token = Token.generate!(:action => 'api')
484 user.api_token = token
484 user.api_token = token
485 user.save
485 user.save
486
486
487 assert_nil User.find_by_api_key(token.value)
487 assert_nil User.find_by_api_key(token.value)
488 end
488 end
489
489
490 should "return the user if the key is found for an active user" do
490 should "return the user if the key is found for an active user" do
491 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
491 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
492 token = Token.generate!(:action => 'api')
492 token = Token.generate!(:action => 'api')
493 user.api_token = token
493 user.api_token = token
494 user.save
494 user.save
495
495
496 assert_equal user, User.find_by_api_key(token.value)
496 assert_equal user, User.find_by_api_key(token.value)
497 end
497 end
498 end
498 end
499
499
500 def test_roles_for_project
500 def test_roles_for_project
501 # user with a role
501 # user with a role
502 roles = @jsmith.roles_for_project(Project.find(1))
502 roles = @jsmith.roles_for_project(Project.find(1))
503 assert_kind_of Role, roles.first
503 assert_kind_of Role, roles.first
504 assert_equal "Manager", roles.first.name
504 assert_equal "Manager", roles.first.name
505
505
506 # user with no role
506 # user with no role
507 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
507 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
508 end
508 end
509
509
510 def test_valid_notification_options
510 def test_valid_notification_options
511 # without memberships
511 # without memberships
512 assert_equal 5, User.find(7).valid_notification_options.size
512 assert_equal 5, User.find(7).valid_notification_options.size
513 # with memberships
513 # with memberships
514 assert_equal 6, User.find(2).valid_notification_options.size
514 assert_equal 6, User.find(2).valid_notification_options.size
515 end
515 end
516
516
517 def test_valid_notification_options_class_method
517 def test_valid_notification_options_class_method
518 assert_equal 5, User.valid_notification_options.size
518 assert_equal 5, User.valid_notification_options.size
519 assert_equal 5, User.valid_notification_options(User.find(7)).size
519 assert_equal 5, User.valid_notification_options(User.find(7)).size
520 assert_equal 6, User.valid_notification_options(User.find(2)).size
520 assert_equal 6, User.valid_notification_options(User.find(2)).size
521 end
521 end
522
522
523 def test_mail_notification_all
523 def test_mail_notification_all
524 @jsmith.mail_notification = 'all'
524 @jsmith.mail_notification = 'all'
525 @jsmith.notified_project_ids = []
525 @jsmith.notified_project_ids = []
526 @jsmith.save
526 @jsmith.save
527 @jsmith.reload
527 @jsmith.reload
528 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
528 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
529 end
529 end
530
530
531 def test_mail_notification_selected
531 def test_mail_notification_selected
532 @jsmith.mail_notification = 'selected'
532 @jsmith.mail_notification = 'selected'
533 @jsmith.notified_project_ids = [1]
533 @jsmith.notified_project_ids = [1]
534 @jsmith.save
534 @jsmith.save
535 @jsmith.reload
535 @jsmith.reload
536 assert Project.find(1).recipients.include?(@jsmith.mail)
536 assert Project.find(1).recipients.include?(@jsmith.mail)
537 end
537 end
538
538
539 def test_mail_notification_only_my_events
539 def test_mail_notification_only_my_events
540 @jsmith.mail_notification = 'only_my_events'
540 @jsmith.mail_notification = 'only_my_events'
541 @jsmith.notified_project_ids = []
541 @jsmith.notified_project_ids = []
542 @jsmith.save
542 @jsmith.save
543 @jsmith.reload
543 @jsmith.reload
544 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
544 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
545 end
545 end
546
546
547 def test_comments_sorting_preference
547 def test_comments_sorting_preference
548 assert !@jsmith.wants_comments_in_reverse_order?
548 assert !@jsmith.wants_comments_in_reverse_order?
549 @jsmith.pref.comments_sorting = 'asc'
549 @jsmith.pref.comments_sorting = 'asc'
550 assert !@jsmith.wants_comments_in_reverse_order?
550 assert !@jsmith.wants_comments_in_reverse_order?
551 @jsmith.pref.comments_sorting = 'desc'
551 @jsmith.pref.comments_sorting = 'desc'
552 assert @jsmith.wants_comments_in_reverse_order?
552 assert @jsmith.wants_comments_in_reverse_order?
553 end
553 end
554
554
555 def test_find_by_mail_should_be_case_insensitive
555 def test_find_by_mail_should_be_case_insensitive
556 u = User.find_by_mail('JSmith@somenet.foo')
556 u = User.find_by_mail('JSmith@somenet.foo')
557 assert_not_nil u
557 assert_not_nil u
558 assert_equal 'jsmith@somenet.foo', u.mail
558 assert_equal 'jsmith@somenet.foo', u.mail
559 end
559 end
560
560
561 def test_random_password
561 def test_random_password
562 u = User.new
562 u = User.new
563 u.random_password
563 u.random_password
564 assert !u.password.blank?
564 assert !u.password.blank?
565 assert !u.password_confirmation.blank?
565 assert !u.password_confirmation.blank?
566 end
566 end
567
567
568 context "#change_password_allowed?" do
568 context "#change_password_allowed?" do
569 should "be allowed if no auth source is set" do
569 should "be allowed if no auth source is set" do
570 user = User.generate_with_protected!
570 user = User.generate_with_protected!
571 assert user.change_password_allowed?
571 assert user.change_password_allowed?
572 end
572 end
573
573
574 should "delegate to the auth source" do
574 should "delegate to the auth source" do
575 user = User.generate_with_protected!
575 user = User.generate_with_protected!
576
576
577 allowed_auth_source = AuthSource.generate!
577 allowed_auth_source = AuthSource.generate!
578 def allowed_auth_source.allow_password_changes?; true; end
578 def allowed_auth_source.allow_password_changes?; true; end
579
579
580 denied_auth_source = AuthSource.generate!
580 denied_auth_source = AuthSource.generate!
581 def denied_auth_source.allow_password_changes?; false; end
581 def denied_auth_source.allow_password_changes?; false; end
582
582
583 assert user.change_password_allowed?
583 assert user.change_password_allowed?
584
584
585 user.auth_source = allowed_auth_source
585 user.auth_source = allowed_auth_source
586 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
586 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
587
587
588 user.auth_source = denied_auth_source
588 user.auth_source = denied_auth_source
589 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
589 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
590 end
590 end
591
591
592 end
592 end
593
593
594 context "#allowed_to?" do
594 context "#allowed_to?" do
595 context "with a unique project" do
595 context "with a unique project" do
596 should "return false if project is archived" do
596 should "return false if project is archived" do
597 project = Project.find(1)
597 project = Project.find(1)
598 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
598 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
599 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
599 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
600 end
600 end
601
601
602 should "return false if related module is disabled" do
602 should "return false if related module is disabled" do
603 project = Project.find(1)
603 project = Project.find(1)
604 project.enabled_module_names = ["issue_tracking"]
604 project.enabled_module_names = ["issue_tracking"]
605 assert @admin.allowed_to?(:add_issues, project)
605 assert @admin.allowed_to?(:add_issues, project)
606 assert ! @admin.allowed_to?(:view_wiki_pages, project)
606 assert ! @admin.allowed_to?(:view_wiki_pages, project)
607 end
607 end
608
608
609 should "authorize nearly everything for admin users" do
609 should "authorize nearly everything for admin users" do
610 project = Project.find(1)
610 project = Project.find(1)
611 assert ! @admin.member_of?(project)
611 assert ! @admin.member_of?(project)
612 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
612 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
613 assert @admin.allowed_to?(p.to_sym, project)
613 assert @admin.allowed_to?(p.to_sym, project)
614 end
614 end
615 end
615 end
616
616
617 should "authorize normal users depending on their roles" do
617 should "authorize normal users depending on their roles" do
618 project = Project.find(1)
618 project = Project.find(1)
619 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
619 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
620 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
620 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
621 end
621 end
622 end
622 end
623
623
624 context "with multiple projects" do
624 context "with multiple projects" do
625 should "return false if array is empty" do
625 should "return false if array is empty" do
626 assert ! @admin.allowed_to?(:view_project, [])
626 assert ! @admin.allowed_to?(:view_project, [])
627 end
627 end
628
628
629 should "return true only if user has permission on all these projects" do
629 should "return true only if user has permission on all these projects" do
630 assert @admin.allowed_to?(:view_project, Project.all)
630 assert @admin.allowed_to?(:view_project, Project.all)
631 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
631 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
632 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
632 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
633 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
633 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
634 end
634 end
635
635
636 should "behave correctly with arrays of 1 project" do
636 should "behave correctly with arrays of 1 project" do
637 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
637 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
638 end
638 end
639 end
639 end
640
640
641 context "with options[:global]" do
641 context "with options[:global]" do
642 should "authorize if user has at least one role that has this permission" do
642 should "authorize if user has at least one role that has this permission" do
643 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
643 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
644 @anonymous = User.find(6)
644 @anonymous = User.find(6)
645 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
645 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
646 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
646 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
647 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
647 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
648 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
648 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
649 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
649 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
650 end
650 end
651 end
651 end
652 end
652 end
653
653
654 context "User#notify_about?" do
654 context "User#notify_about?" do
655 context "Issues" do
655 context "Issues" do
656 setup do
656 setup do
657 @project = Project.find(1)
657 @project = Project.find(1)
658 @author = User.generate_with_protected!
658 @author = User.generate_with_protected!
659 @assignee = User.generate_with_protected!
659 @assignee = User.generate_with_protected!
660 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
660 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
661 end
661 end
662
662
663 should "be true for a user with :all" do
663 should "be true for a user with :all" do
664 @author.update_attribute(:mail_notification, 'all')
664 @author.update_attribute(:mail_notification, 'all')
665 assert @author.notify_about?(@issue)
665 assert @author.notify_about?(@issue)
666 end
666 end
667
667
668 should "be false for a user with :none" do
668 should "be false for a user with :none" do
669 @author.update_attribute(:mail_notification, 'none')
669 @author.update_attribute(:mail_notification, 'none')
670 assert ! @author.notify_about?(@issue)
670 assert ! @author.notify_about?(@issue)
671 end
671 end
672
672
673 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
673 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
674 @user = User.generate_with_protected!(:mail_notification => 'only_my_events')
674 @user = User.generate_with_protected!(:mail_notification => 'only_my_events')
675 Member.create!(:user => @user, :project => @project, :role_ids => [1])
675 assert ! @user.notify_about?(@issue)
676 assert ! @user.notify_about?(@issue)
676 end
677 end
677
678
678 should "be true for a user with :only_my_events and is the author" do
679 should "be true for a user with :only_my_events and is the author" do
679 @author.update_attribute(:mail_notification, 'only_my_events')
680 @author.update_attribute(:mail_notification, 'only_my_events')
680 assert @author.notify_about?(@issue)
681 assert @author.notify_about?(@issue)
681 end
682 end
682
683
683 should "be true for a user with :only_my_events and is the assignee" do
684 should "be true for a user with :only_my_events and is the assignee" do
684 @assignee.update_attribute(:mail_notification, 'only_my_events')
685 @assignee.update_attribute(:mail_notification, 'only_my_events')
685 assert @assignee.notify_about?(@issue)
686 assert @assignee.notify_about?(@issue)
686 end
687 end
687
688
688 should "be true for a user with :only_assigned and is the assignee" do
689 should "be true for a user with :only_assigned and is the assignee" do
689 @assignee.update_attribute(:mail_notification, 'only_assigned')
690 @assignee.update_attribute(:mail_notification, 'only_assigned')
690 assert @assignee.notify_about?(@issue)
691 assert @assignee.notify_about?(@issue)
691 end
692 end
692
693
693 should "be false for a user with :only_assigned and is not the assignee" do
694 should "be false for a user with :only_assigned and is not the assignee" do
694 @author.update_attribute(:mail_notification, 'only_assigned')
695 @author.update_attribute(:mail_notification, 'only_assigned')
695 assert ! @author.notify_about?(@issue)
696 assert ! @author.notify_about?(@issue)
696 end
697 end
697
698
698 should "be true for a user with :only_owner and is the author" do
699 should "be true for a user with :only_owner and is the author" do
699 @author.update_attribute(:mail_notification, 'only_owner')
700 @author.update_attribute(:mail_notification, 'only_owner')
700 assert @author.notify_about?(@issue)
701 assert @author.notify_about?(@issue)
701 end
702 end
702
703
703 should "be false for a user with :only_owner and is not the author" do
704 should "be false for a user with :only_owner and is not the author" do
704 @assignee.update_attribute(:mail_notification, 'only_owner')
705 @assignee.update_attribute(:mail_notification, 'only_owner')
705 assert ! @assignee.notify_about?(@issue)
706 assert ! @assignee.notify_about?(@issue)
706 end
707 end
708
709 should "be true for a user with :selected and is the author" do
710 @author.update_attribute(:mail_notification, 'selected')
711 assert @author.notify_about?(@issue)
712 end
713
714 should "be true for a user with :selected and is the assignee" do
715 @assignee.update_attribute(:mail_notification, 'selected')
716 assert @assignee.notify_about?(@issue)
717 end
718
719 should "be false for a user with :selected and is not the author or assignee" do
720 @user = User.generate_with_protected!(:mail_notification => 'selected')
721 Member.create!(:user => @user, :project => @project, :role_ids => [1])
722 assert ! @user.notify_about?(@issue)
723 end
707 end
724 end
708
725
709 context "other events" do
726 context "other events" do
710 should 'be added and tested'
727 should 'be added and tested'
711 end
728 end
712 end
729 end
713
730
714 if Object.const_defined?(:OpenID)
731 if Object.const_defined?(:OpenID)
715
732
716 def test_setting_identity_url
733 def test_setting_identity_url
717 normalized_open_id_url = 'http://example.com/'
734 normalized_open_id_url = 'http://example.com/'
718 u = User.new( :identity_url => 'http://example.com/' )
735 u = User.new( :identity_url => 'http://example.com/' )
719 assert_equal normalized_open_id_url, u.identity_url
736 assert_equal normalized_open_id_url, u.identity_url
720 end
737 end
721
738
722 def test_setting_identity_url_without_trailing_slash
739 def test_setting_identity_url_without_trailing_slash
723 normalized_open_id_url = 'http://example.com/'
740 normalized_open_id_url = 'http://example.com/'
724 u = User.new( :identity_url => 'http://example.com' )
741 u = User.new( :identity_url => 'http://example.com' )
725 assert_equal normalized_open_id_url, u.identity_url
742 assert_equal normalized_open_id_url, u.identity_url
726 end
743 end
727
744
728 def test_setting_identity_url_without_protocol
745 def test_setting_identity_url_without_protocol
729 normalized_open_id_url = 'http://example.com/'
746 normalized_open_id_url = 'http://example.com/'
730 u = User.new( :identity_url => 'example.com' )
747 u = User.new( :identity_url => 'example.com' )
731 assert_equal normalized_open_id_url, u.identity_url
748 assert_equal normalized_open_id_url, u.identity_url
732 end
749 end
733
750
734 def test_setting_blank_identity_url
751 def test_setting_blank_identity_url
735 u = User.new( :identity_url => 'example.com' )
752 u = User.new( :identity_url => 'example.com' )
736 u.identity_url = ''
753 u.identity_url = ''
737 assert u.identity_url.blank?
754 assert u.identity_url.blank?
738 end
755 end
739
756
740 def test_setting_invalid_identity_url
757 def test_setting_invalid_identity_url
741 u = User.new( :identity_url => 'this is not an openid url' )
758 u = User.new( :identity_url => 'this is not an openid url' )
742 assert u.identity_url.blank?
759 assert u.identity_url.blank?
743 end
760 end
744
761
745 else
762 else
746 puts "Skipping openid tests."
763 puts "Skipping openid tests."
747 end
764 end
748
765
749 end
766 end
General Comments 0
You need to be logged in to leave comments. Login now