##// END OF EJS Templates
Merged r3906 from trunk....
Eric Davis -
r3842:8dde6e019d04
parent child
Show More
@@ -1,272 +1,272
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AccountController < ApplicationController
18 class AccountController < ApplicationController
19 helper :custom_fields
19 helper :custom_fields
20 include CustomFieldsHelper
20 include CustomFieldsHelper
21
21
22 # prevents login action to be filtered by check_if_login_required application scope filter
22 # prevents login action to be filtered by check_if_login_required application scope filter
23 skip_before_filter :check_if_login_required
23 skip_before_filter :check_if_login_required
24
24
25 # Login request and validation
25 # Login request and validation
26 def login
26 def login
27 if request.get?
27 if request.get?
28 logout_user
28 logout_user
29 else
29 else
30 authenticate_user
30 authenticate_user
31 end
31 end
32 end
32 end
33
33
34 # Log out current user and redirect to welcome page
34 # Log out current user and redirect to welcome page
35 def logout
35 def logout
36 logout_user
36 logout_user
37 redirect_to home_url
37 redirect_to home_url
38 end
38 end
39
39
40 # Enable user to choose a new password
40 # Enable user to choose a new password
41 def lost_password
41 def lost_password
42 redirect_to(home_url) && return unless Setting.lost_password?
42 redirect_to(home_url) && return unless Setting.lost_password?
43 if params[:token]
43 if params[:token]
44 @token = Token.find_by_action_and_value("recovery", params[:token])
44 @token = Token.find_by_action_and_value("recovery", params[:token])
45 redirect_to(home_url) && return unless @token and !@token.expired?
45 redirect_to(home_url) && return unless @token and !@token.expired?
46 @user = @token.user
46 @user = @token.user
47 if request.post?
47 if request.post?
48 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
48 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
49 if @user.save
49 if @user.save
50 @token.destroy
50 @token.destroy
51 flash[:notice] = l(:notice_account_password_updated)
51 flash[:notice] = l(:notice_account_password_updated)
52 redirect_to :action => 'login'
52 redirect_to :action => 'login'
53 return
53 return
54 end
54 end
55 end
55 end
56 render :template => "account/password_recovery"
56 render :template => "account/password_recovery"
57 return
57 return
58 else
58 else
59 if request.post?
59 if request.post?
60 user = User.find_by_mail(params[:mail])
60 user = User.find_by_mail(params[:mail])
61 # user not found in db
61 # user not found in db
62 (flash.now[:error] = l(:notice_account_unknown_email); return) unless user
62 (flash.now[:error] = l(:notice_account_unknown_email); return) unless user
63 # user uses an external authentification
63 # user uses an external authentification
64 (flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id
64 (flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id
65 # create a new token for password recovery
65 # create a new token for password recovery
66 token = Token.new(:user => user, :action => "recovery")
66 token = Token.new(:user => user, :action => "recovery")
67 if token.save
67 if token.save
68 Mailer.deliver_lost_password(token)
68 Mailer.deliver_lost_password(token)
69 flash[:notice] = l(:notice_account_lost_email_sent)
69 flash[:notice] = l(:notice_account_lost_email_sent)
70 redirect_to :action => 'login'
70 redirect_to :action => 'login'
71 return
71 return
72 end
72 end
73 end
73 end
74 end
74 end
75 end
75 end
76
76
77 # User self-registration
77 # User self-registration
78 def register
78 def register
79 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
79 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
80 if request.get?
80 if request.get?
81 session[:auth_source_registration] = nil
81 session[:auth_source_registration] = nil
82 @user = User.new(:language => Setting.default_language)
82 @user = User.new(:language => Setting.default_language)
83 else
83 else
84 @user = User.new(params[:user])
84 @user = User.new(params[:user])
85 @user.admin = false
85 @user.admin = false
86 @user.status = User::STATUS_REGISTERED
86 @user.register
87 if session[:auth_source_registration]
87 if session[:auth_source_registration]
88 @user.status = User::STATUS_ACTIVE
88 @user.activate
89 @user.login = session[:auth_source_registration][:login]
89 @user.login = session[:auth_source_registration][:login]
90 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
90 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
91 if @user.save
91 if @user.save
92 session[:auth_source_registration] = nil
92 session[:auth_source_registration] = nil
93 self.logged_user = @user
93 self.logged_user = @user
94 flash[:notice] = l(:notice_account_activated)
94 flash[:notice] = l(:notice_account_activated)
95 redirect_to :controller => 'my', :action => 'account'
95 redirect_to :controller => 'my', :action => 'account'
96 end
96 end
97 else
97 else
98 @user.login = params[:user][:login]
98 @user.login = params[:user][:login]
99 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
99 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
100
100
101 case Setting.self_registration
101 case Setting.self_registration
102 when '1'
102 when '1'
103 register_by_email_activation(@user)
103 register_by_email_activation(@user)
104 when '3'
104 when '3'
105 register_automatically(@user)
105 register_automatically(@user)
106 else
106 else
107 register_manually_by_administrator(@user)
107 register_manually_by_administrator(@user)
108 end
108 end
109 end
109 end
110 end
110 end
111 end
111 end
112
112
113 # Token based account activation
113 # Token based account activation
114 def activate
114 def activate
115 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
115 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
116 token = Token.find_by_action_and_value('register', params[:token])
116 token = Token.find_by_action_and_value('register', params[:token])
117 redirect_to(home_url) && return unless token and !token.expired?
117 redirect_to(home_url) && return unless token and !token.expired?
118 user = token.user
118 user = token.user
119 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
119 redirect_to(home_url) && return unless user.registered?
120 user.status = User::STATUS_ACTIVE
120 user.activate
121 if user.save
121 if user.save
122 token.destroy
122 token.destroy
123 flash[:notice] = l(:notice_account_activated)
123 flash[:notice] = l(:notice_account_activated)
124 end
124 end
125 redirect_to :action => 'login'
125 redirect_to :action => 'login'
126 end
126 end
127
127
128 private
128 private
129
129
130 def logout_user
130 def logout_user
131 if User.current.logged?
131 if User.current.logged?
132 cookies.delete :autologin
132 cookies.delete :autologin
133 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
133 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
134 self.logged_user = nil
134 self.logged_user = nil
135 end
135 end
136 end
136 end
137
137
138 def authenticate_user
138 def authenticate_user
139 if Setting.openid? && using_open_id?
139 if Setting.openid? && using_open_id?
140 open_id_authenticate(params[:openid_url])
140 open_id_authenticate(params[:openid_url])
141 else
141 else
142 password_authentication
142 password_authentication
143 end
143 end
144 end
144 end
145
145
146 def password_authentication
146 def password_authentication
147 user = User.try_to_login(params[:username], params[:password])
147 user = User.try_to_login(params[:username], params[:password])
148
148
149 if user.nil?
149 if user.nil?
150 invalid_credentials
150 invalid_credentials
151 elsif user.new_record?
151 elsif user.new_record?
152 onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id })
152 onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id })
153 else
153 else
154 # Valid user
154 # Valid user
155 successful_authentication(user)
155 successful_authentication(user)
156 end
156 end
157 end
157 end
158
158
159
159
160 def open_id_authenticate(openid_url)
160 def open_id_authenticate(openid_url)
161 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
161 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
162 if result.successful?
162 if result.successful?
163 user = User.find_or_initialize_by_identity_url(identity_url)
163 user = User.find_or_initialize_by_identity_url(identity_url)
164 if user.new_record?
164 if user.new_record?
165 # Self-registration off
165 # Self-registration off
166 redirect_to(home_url) && return unless Setting.self_registration?
166 redirect_to(home_url) && return unless Setting.self_registration?
167
167
168 # Create on the fly
168 # Create on the fly
169 user.login = registration['nickname'] unless registration['nickname'].nil?
169 user.login = registration['nickname'] unless registration['nickname'].nil?
170 user.mail = registration['email'] unless registration['email'].nil?
170 user.mail = registration['email'] unless registration['email'].nil?
171 user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
171 user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
172 user.random_password
172 user.random_password
173 user.status = User::STATUS_REGISTERED
173 user.register
174
174
175 case Setting.self_registration
175 case Setting.self_registration
176 when '1'
176 when '1'
177 register_by_email_activation(user) do
177 register_by_email_activation(user) do
178 onthefly_creation_failed(user)
178 onthefly_creation_failed(user)
179 end
179 end
180 when '3'
180 when '3'
181 register_automatically(user) do
181 register_automatically(user) do
182 onthefly_creation_failed(user)
182 onthefly_creation_failed(user)
183 end
183 end
184 else
184 else
185 register_manually_by_administrator(user) do
185 register_manually_by_administrator(user) do
186 onthefly_creation_failed(user)
186 onthefly_creation_failed(user)
187 end
187 end
188 end
188 end
189 else
189 else
190 # Existing record
190 # Existing record
191 if user.active?
191 if user.active?
192 successful_authentication(user)
192 successful_authentication(user)
193 else
193 else
194 account_pending
194 account_pending
195 end
195 end
196 end
196 end
197 end
197 end
198 end
198 end
199 end
199 end
200
200
201 def successful_authentication(user)
201 def successful_authentication(user)
202 # Valid user
202 # Valid user
203 self.logged_user = user
203 self.logged_user = user
204 # generate a key and set cookie if autologin
204 # generate a key and set cookie if autologin
205 if params[:autologin] && Setting.autologin?
205 if params[:autologin] && Setting.autologin?
206 token = Token.create(:user => user, :action => 'autologin')
206 token = Token.create(:user => user, :action => 'autologin')
207 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
207 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
208 end
208 end
209 call_hook(:controller_account_success_authentication_after, {:user => user })
209 call_hook(:controller_account_success_authentication_after, {:user => user })
210 redirect_back_or_default :controller => 'my', :action => 'page'
210 redirect_back_or_default :controller => 'my', :action => 'page'
211 end
211 end
212
212
213 # Onthefly creation failed, display the registration form to fill/fix attributes
213 # Onthefly creation failed, display the registration form to fill/fix attributes
214 def onthefly_creation_failed(user, auth_source_options = { })
214 def onthefly_creation_failed(user, auth_source_options = { })
215 @user = user
215 @user = user
216 session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
216 session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
217 render :action => 'register'
217 render :action => 'register'
218 end
218 end
219
219
220 def invalid_credentials
220 def invalid_credentials
221 logger.warn "Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}"
221 logger.warn "Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}"
222 flash.now[:error] = l(:notice_account_invalid_creditentials)
222 flash.now[:error] = l(:notice_account_invalid_creditentials)
223 end
223 end
224
224
225 # Register a user for email activation.
225 # Register a user for email activation.
226 #
226 #
227 # Pass a block for behavior when a user fails to save
227 # Pass a block for behavior when a user fails to save
228 def register_by_email_activation(user, &block)
228 def register_by_email_activation(user, &block)
229 token = Token.new(:user => user, :action => "register")
229 token = Token.new(:user => user, :action => "register")
230 if user.save and token.save
230 if user.save and token.save
231 Mailer.deliver_register(token)
231 Mailer.deliver_register(token)
232 flash[:notice] = l(:notice_account_register_done)
232 flash[:notice] = l(:notice_account_register_done)
233 redirect_to :action => 'login'
233 redirect_to :action => 'login'
234 else
234 else
235 yield if block_given?
235 yield if block_given?
236 end
236 end
237 end
237 end
238
238
239 # Automatically register a user
239 # Automatically register a user
240 #
240 #
241 # Pass a block for behavior when a user fails to save
241 # Pass a block for behavior when a user fails to save
242 def register_automatically(user, &block)
242 def register_automatically(user, &block)
243 # Automatic activation
243 # Automatic activation
244 user.status = User::STATUS_ACTIVE
244 user.activate
245 user.last_login_on = Time.now
245 user.last_login_on = Time.now
246 if user.save
246 if user.save
247 self.logged_user = user
247 self.logged_user = user
248 flash[:notice] = l(:notice_account_activated)
248 flash[:notice] = l(:notice_account_activated)
249 redirect_to :controller => 'my', :action => 'account'
249 redirect_to :controller => 'my', :action => 'account'
250 else
250 else
251 yield if block_given?
251 yield if block_given?
252 end
252 end
253 end
253 end
254
254
255 # Manual activation by the administrator
255 # Manual activation by the administrator
256 #
256 #
257 # Pass a block for behavior when a user fails to save
257 # Pass a block for behavior when a user fails to save
258 def register_manually_by_administrator(user, &block)
258 def register_manually_by_administrator(user, &block)
259 if user.save
259 if user.save
260 # Sends an email to the administrators
260 # Sends an email to the administrators
261 Mailer.deliver_account_activation_request(user)
261 Mailer.deliver_account_activation_request(user)
262 account_pending
262 account_pending
263 else
263 else
264 yield if block_given?
264 yield if block_given?
265 end
265 end
266 end
266 end
267
267
268 def account_pending
268 def account_pending
269 flash[:notice] = l(:notice_account_pending)
269 flash[:notice] = l(:notice_account_pending)
270 redirect_to :action => 'login'
270 redirect_to :action => 'login'
271 end
271 end
272 end
272 end
@@ -1,386 +1,410
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < Principal
20 class User < Principal
21
21
22 # Account statuses
22 # Account statuses
23 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
24 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
25 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
26 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
27
27
28 USER_FORMATS = {
28 USER_FORMATS = {
29 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :username => '#{login}'
33 :username => '#{login}'
34 }
34 }
35
35
36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 has_many :changesets, :dependent => :nullify
39 has_many :changesets, :dependent => :nullify
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
42 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
43 belongs_to :auth_source
43 belongs_to :auth_source
44
44
45 # Active non-anonymous users scope
45 # Active non-anonymous users scope
46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
47
47
48 acts_as_customizable
48 acts_as_customizable
49
49
50 attr_accessor :password, :password_confirmation
50 attr_accessor :password, :password_confirmation
51 attr_accessor :last_before_login_on
51 attr_accessor :last_before_login_on
52 # Prevents unauthorized assignments
52 # Prevents unauthorized assignments
53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
54
54
55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
58 # Login must contain lettres, numbers, underscores only
58 # Login must contain lettres, numbers, underscores only
59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
60 validates_length_of :login, :maximum => 30
60 validates_length_of :login, :maximum => 30
61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
62 validates_length_of :firstname, :lastname, :maximum => 30
62 validates_length_of :firstname, :lastname, :maximum => 30
63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
64 validates_length_of :mail, :maximum => 60, :allow_nil => true
64 validates_length_of :mail, :maximum => 60, :allow_nil => true
65 validates_confirmation_of :password, :allow_nil => true
65 validates_confirmation_of :password, :allow_nil => true
66
66
67 def before_create
67 def before_create
68 self.mail_notification = false
68 self.mail_notification = false
69 true
69 true
70 end
70 end
71
71
72 def before_save
72 def before_save
73 # update hashed_password if password was set
73 # update hashed_password if password was set
74 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
74 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
75 end
75 end
76
76
77 def reload(*args)
77 def reload(*args)
78 @name = nil
78 @name = nil
79 super
79 super
80 end
80 end
81
81
82 def mail=(arg)
82 def mail=(arg)
83 write_attribute(:mail, arg.to_s.strip)
83 write_attribute(:mail, arg.to_s.strip)
84 end
84 end
85
85
86 def identity_url=(url)
86 def identity_url=(url)
87 if url.blank?
87 if url.blank?
88 write_attribute(:identity_url, '')
88 write_attribute(:identity_url, '')
89 else
89 else
90 begin
90 begin
91 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
91 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
92 rescue OpenIdAuthentication::InvalidOpenId
92 rescue OpenIdAuthentication::InvalidOpenId
93 # Invlaid url, don't save
93 # Invlaid url, don't save
94 end
94 end
95 end
95 end
96 self.read_attribute(:identity_url)
96 self.read_attribute(:identity_url)
97 end
97 end
98
98
99 # Returns the user that matches provided login and password, or nil
99 # Returns the user that matches provided login and password, or nil
100 def self.try_to_login(login, password)
100 def self.try_to_login(login, password)
101 # Make sure no one can sign in with an empty password
101 # Make sure no one can sign in with an empty password
102 return nil if password.to_s.empty?
102 return nil if password.to_s.empty?
103 user = find_by_login(login)
103 user = find_by_login(login)
104 if user
104 if user
105 # user is already in local database
105 # user is already in local database
106 return nil if !user.active?
106 return nil if !user.active?
107 if user.auth_source
107 if user.auth_source
108 # user has an external authentication method
108 # user has an external authentication method
109 return nil unless user.auth_source.authenticate(login, password)
109 return nil unless user.auth_source.authenticate(login, password)
110 else
110 else
111 # authentication with local password
111 # authentication with local password
112 return nil unless User.hash_password(password) == user.hashed_password
112 return nil unless User.hash_password(password) == user.hashed_password
113 end
113 end
114 else
114 else
115 # user is not yet registered, try to authenticate with available sources
115 # user is not yet registered, try to authenticate with available sources
116 attrs = AuthSource.authenticate(login, password)
116 attrs = AuthSource.authenticate(login, password)
117 if attrs
117 if attrs
118 user = new(attrs)
118 user = new(attrs)
119 user.login = login
119 user.login = login
120 user.language = Setting.default_language
120 user.language = Setting.default_language
121 if user.save
121 if user.save
122 user.reload
122 user.reload
123 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
123 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
124 end
124 end
125 end
125 end
126 end
126 end
127 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
127 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
128 user
128 user
129 rescue => text
129 rescue => text
130 raise text
130 raise text
131 end
131 end
132
132
133 # Returns the user who matches the given autologin +key+ or nil
133 # Returns the user who matches the given autologin +key+ or nil
134 def self.try_to_autologin(key)
134 def self.try_to_autologin(key)
135 tokens = Token.find_all_by_action_and_value('autologin', key)
135 tokens = Token.find_all_by_action_and_value('autologin', key)
136 # Make sure there's only 1 token that matches the key
136 # Make sure there's only 1 token that matches the key
137 if tokens.size == 1
137 if tokens.size == 1
138 token = tokens.first
138 token = tokens.first
139 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
139 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
140 token.user.update_attribute(:last_login_on, Time.now)
140 token.user.update_attribute(:last_login_on, Time.now)
141 token.user
141 token.user
142 end
142 end
143 end
143 end
144 end
144 end
145
145
146 # Return user's full name for display
146 # Return user's full name for display
147 def name(formatter = nil)
147 def name(formatter = nil)
148 if formatter
148 if formatter
149 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
149 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
150 else
150 else
151 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
151 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
152 end
152 end
153 end
153 end
154
154
155 def active?
155 def active?
156 self.status == STATUS_ACTIVE
156 self.status == STATUS_ACTIVE
157 end
157 end
158
158
159 def registered?
159 def registered?
160 self.status == STATUS_REGISTERED
160 self.status == STATUS_REGISTERED
161 end
161 end
162
162
163 def locked?
163 def locked?
164 self.status == STATUS_LOCKED
164 self.status == STATUS_LOCKED
165 end
165 end
166
166
167 def activate
168 self.status = STATUS_ACTIVE
169 end
170
171 def register
172 self.status = STATUS_REGISTERED
173 end
174
175 def lock
176 self.status = STATUS_LOCKED
177 end
178
179 def activate!
180 update_attribute(:status, STATUS_ACTIVE)
181 end
182
183 def register!
184 update_attribute(:status, STATUS_REGISTERED)
185 end
186
187 def lock!
188 update_attribute(:status, STATUS_LOCKED)
189 end
190
167 def check_password?(clear_password)
191 def check_password?(clear_password)
168 if auth_source_id.present?
192 if auth_source_id.present?
169 auth_source.authenticate(self.login, clear_password)
193 auth_source.authenticate(self.login, clear_password)
170 else
194 else
171 User.hash_password(clear_password) == self.hashed_password
195 User.hash_password(clear_password) == self.hashed_password
172 end
196 end
173 end
197 end
174
198
175 # Does the backend storage allow this user to change their password?
199 # Does the backend storage allow this user to change their password?
176 def change_password_allowed?
200 def change_password_allowed?
177 return true if auth_source_id.blank?
201 return true if auth_source_id.blank?
178 return auth_source.allow_password_changes?
202 return auth_source.allow_password_changes?
179 end
203 end
180
204
181 # Generate and set a random password. Useful for automated user creation
205 # Generate and set a random password. Useful for automated user creation
182 # Based on Token#generate_token_value
206 # Based on Token#generate_token_value
183 #
207 #
184 def random_password
208 def random_password
185 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
209 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
186 password = ''
210 password = ''
187 40.times { |i| password << chars[rand(chars.size-1)] }
211 40.times { |i| password << chars[rand(chars.size-1)] }
188 self.password = password
212 self.password = password
189 self.password_confirmation = password
213 self.password_confirmation = password
190 self
214 self
191 end
215 end
192
216
193 def pref
217 def pref
194 self.preference ||= UserPreference.new(:user => self)
218 self.preference ||= UserPreference.new(:user => self)
195 end
219 end
196
220
197 def time_zone
221 def time_zone
198 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
222 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
199 end
223 end
200
224
201 def wants_comments_in_reverse_order?
225 def wants_comments_in_reverse_order?
202 self.pref[:comments_sorting] == 'desc'
226 self.pref[:comments_sorting] == 'desc'
203 end
227 end
204
228
205 # Return user's RSS key (a 40 chars long string), used to access feeds
229 # Return user's RSS key (a 40 chars long string), used to access feeds
206 def rss_key
230 def rss_key
207 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
231 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
208 token.value
232 token.value
209 end
233 end
210
234
211 # Return user's API key (a 40 chars long string), used to access the API
235 # Return user's API key (a 40 chars long string), used to access the API
212 def api_key
236 def api_key
213 token = self.api_token || self.create_api_token(:action => 'api')
237 token = self.api_token || self.create_api_token(:action => 'api')
214 token.value
238 token.value
215 end
239 end
216
240
217 # Return an array of project ids for which the user has explicitly turned mail notifications on
241 # Return an array of project ids for which the user has explicitly turned mail notifications on
218 def notified_projects_ids
242 def notified_projects_ids
219 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
243 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
220 end
244 end
221
245
222 def notified_project_ids=(ids)
246 def notified_project_ids=(ids)
223 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
247 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
224 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
248 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
225 @notified_projects_ids = nil
249 @notified_projects_ids = nil
226 notified_projects_ids
250 notified_projects_ids
227 end
251 end
228
252
229 # Find a user account by matching the exact login and then a case-insensitive
253 # Find a user account by matching the exact login and then a case-insensitive
230 # version. Exact matches will be given priority.
254 # version. Exact matches will be given priority.
231 def self.find_by_login(login)
255 def self.find_by_login(login)
232 # force string comparison to be case sensitive on MySQL
256 # force string comparison to be case sensitive on MySQL
233 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
257 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
234
258
235 # First look for an exact match
259 # First look for an exact match
236 user = first(:conditions => ["#{type_cast} login = ?", login])
260 user = first(:conditions => ["#{type_cast} login = ?", login])
237 # Fail over to case-insensitive if none was found
261 # Fail over to case-insensitive if none was found
238 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
262 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
239 end
263 end
240
264
241 def self.find_by_rss_key(key)
265 def self.find_by_rss_key(key)
242 token = Token.find_by_value(key)
266 token = Token.find_by_value(key)
243 token && token.user.active? ? token.user : nil
267 token && token.user.active? ? token.user : nil
244 end
268 end
245
269
246 def self.find_by_api_key(key)
270 def self.find_by_api_key(key)
247 token = Token.find_by_action_and_value('api', key)
271 token = Token.find_by_action_and_value('api', key)
248 token && token.user.active? ? token.user : nil
272 token && token.user.active? ? token.user : nil
249 end
273 end
250
274
251 # Makes find_by_mail case-insensitive
275 # Makes find_by_mail case-insensitive
252 def self.find_by_mail(mail)
276 def self.find_by_mail(mail)
253 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
277 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
254 end
278 end
255
279
256 def to_s
280 def to_s
257 name
281 name
258 end
282 end
259
283
260 # Returns the current day according to user's time zone
284 # Returns the current day according to user's time zone
261 def today
285 def today
262 if time_zone.nil?
286 if time_zone.nil?
263 Date.today
287 Date.today
264 else
288 else
265 Time.now.in_time_zone(time_zone).to_date
289 Time.now.in_time_zone(time_zone).to_date
266 end
290 end
267 end
291 end
268
292
269 def logged?
293 def logged?
270 true
294 true
271 end
295 end
272
296
273 def anonymous?
297 def anonymous?
274 !logged?
298 !logged?
275 end
299 end
276
300
277 # Return user's roles for project
301 # Return user's roles for project
278 def roles_for_project(project)
302 def roles_for_project(project)
279 roles = []
303 roles = []
280 # No role on archived projects
304 # No role on archived projects
281 return roles unless project && project.active?
305 return roles unless project && project.active?
282 if logged?
306 if logged?
283 # Find project membership
307 # Find project membership
284 membership = memberships.detect {|m| m.project_id == project.id}
308 membership = memberships.detect {|m| m.project_id == project.id}
285 if membership
309 if membership
286 roles = membership.roles
310 roles = membership.roles
287 else
311 else
288 @role_non_member ||= Role.non_member
312 @role_non_member ||= Role.non_member
289 roles << @role_non_member
313 roles << @role_non_member
290 end
314 end
291 else
315 else
292 @role_anonymous ||= Role.anonymous
316 @role_anonymous ||= Role.anonymous
293 roles << @role_anonymous
317 roles << @role_anonymous
294 end
318 end
295 roles
319 roles
296 end
320 end
297
321
298 # Return true if the user is a member of project
322 # Return true if the user is a member of project
299 def member_of?(project)
323 def member_of?(project)
300 !roles_for_project(project).detect {|role| role.member?}.nil?
324 !roles_for_project(project).detect {|role| role.member?}.nil?
301 end
325 end
302
326
303 # Return true if the user is allowed to do the specified action on project
327 # Return true if the user is allowed to do the specified action on project
304 # action can be:
328 # action can be:
305 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
329 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
306 # * a permission Symbol (eg. :edit_project)
330 # * a permission Symbol (eg. :edit_project)
307 def allowed_to?(action, project, options={})
331 def allowed_to?(action, project, options={})
308 if project
332 if project
309 # No action allowed on archived projects
333 # No action allowed on archived projects
310 return false unless project.active?
334 return false unless project.active?
311 # No action allowed on disabled modules
335 # No action allowed on disabled modules
312 return false unless project.allows_to?(action)
336 return false unless project.allows_to?(action)
313 # Admin users are authorized for anything else
337 # Admin users are authorized for anything else
314 return true if admin?
338 return true if admin?
315
339
316 roles = roles_for_project(project)
340 roles = roles_for_project(project)
317 return false unless roles
341 return false unless roles
318 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
342 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
319
343
320 elsif options[:global]
344 elsif options[:global]
321 # Admin users are always authorized
345 # Admin users are always authorized
322 return true if admin?
346 return true if admin?
323
347
324 # authorize if user has at least one role that has this permission
348 # authorize if user has at least one role that has this permission
325 roles = memberships.collect {|m| m.roles}.flatten.uniq
349 roles = memberships.collect {|m| m.roles}.flatten.uniq
326 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
350 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
327 else
351 else
328 false
352 false
329 end
353 end
330 end
354 end
331
355
332 def self.current=(user)
356 def self.current=(user)
333 @current_user = user
357 @current_user = user
334 end
358 end
335
359
336 def self.current
360 def self.current
337 @current_user ||= User.anonymous
361 @current_user ||= User.anonymous
338 end
362 end
339
363
340 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
364 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
341 # one anonymous user per database.
365 # one anonymous user per database.
342 def self.anonymous
366 def self.anonymous
343 anonymous_user = AnonymousUser.find(:first)
367 anonymous_user = AnonymousUser.find(:first)
344 if anonymous_user.nil?
368 if anonymous_user.nil?
345 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
369 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
346 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
370 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
347 end
371 end
348 anonymous_user
372 anonymous_user
349 end
373 end
350
374
351 protected
375 protected
352
376
353 def validate
377 def validate
354 # Password length validation based on setting
378 # Password length validation based on setting
355 if !password.nil? && password.size < Setting.password_min_length.to_i
379 if !password.nil? && password.size < Setting.password_min_length.to_i
356 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
380 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
357 end
381 end
358 end
382 end
359
383
360 private
384 private
361
385
362 # Return password digest
386 # Return password digest
363 def self.hash_password(clear_password)
387 def self.hash_password(clear_password)
364 Digest::SHA1.hexdigest(clear_password || "")
388 Digest::SHA1.hexdigest(clear_password || "")
365 end
389 end
366 end
390 end
367
391
368 class AnonymousUser < User
392 class AnonymousUser < User
369
393
370 def validate_on_create
394 def validate_on_create
371 # There should be only one AnonymousUser in the database
395 # There should be only one AnonymousUser in the database
372 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
396 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
373 end
397 end
374
398
375 def available_custom_fields
399 def available_custom_fields
376 []
400 []
377 end
401 end
378
402
379 # Overrides a few properties
403 # Overrides a few properties
380 def logged?; false end
404 def logged?; false end
381 def admin; false end
405 def admin; false end
382 def name(*args); I18n.t(:label_user_anonymous) end
406 def name(*args); I18n.t(:label_user_anonymous) end
383 def mail; nil end
407 def mail; nil end
384 def time_zone; nil end
408 def time_zone; nil end
385 def rss_key; nil end
409 def rss_key; nil end
386 end
410 end
General Comments 0
You need to be logged in to leave comments. Login now