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