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