##// END OF EJS Templates
Hooked up on the fly OpenID user creation....
Eric Davis -
r2382:0310f43126ac
parent child
Show More
@@ -1,235 +1,235
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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, :only => [:login, :lost_password, :register, :activate]
23 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
24
24
25 # Show user's account
25 # Show user's account
26 def show
26 def show
27 @user = User.active.find(params[:id])
27 @user = User.active.find(params[:id])
28 @custom_values = @user.custom_values
28 @custom_values = @user.custom_values
29
29
30 # show only public projects and private projects that the logged in user is also a member of
30 # show only public projects and private projects that the logged in user is also a member of
31 @memberships = @user.memberships.select do |membership|
31 @memberships = @user.memberships.select do |membership|
32 membership.project.is_public? || (User.current.member_of?(membership.project))
32 membership.project.is_public? || (User.current.member_of?(membership.project))
33 end
33 end
34
34
35 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
35 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
36 @events_by_day = events.group_by(&:event_date)
36 @events_by_day = events.group_by(&:event_date)
37
37
38 rescue ActiveRecord::RecordNotFound
38 rescue ActiveRecord::RecordNotFound
39 render_404
39 render_404
40 end
40 end
41
41
42 # Login request and validation
42 # Login request and validation
43 def login
43 def login
44 if request.get?
44 if request.get?
45 # Logout user
45 # Logout user
46 self.logged_user = nil
46 self.logged_user = nil
47 else
47 else
48 # Authenticate user
48 # Authenticate user
49 unless using_open_id?
49 unless using_open_id?
50 password_authentication
50 password_authentication
51 else
51 else
52 open_id_authenticate(params[:openid_url])
52 open_id_authenticate(params[:openid_url])
53 end
53 end
54 end
54 end
55 end
55 end
56
56
57 # Log out current user and redirect to welcome page
57 # Log out current user and redirect to welcome page
58 def logout
58 def logout
59 cookies.delete :autologin
59 cookies.delete :autologin
60 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
60 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
61 self.logged_user = nil
61 self.logged_user = nil
62 redirect_to home_url
62 redirect_to home_url
63 end
63 end
64
64
65 # Enable user to choose a new password
65 # Enable user to choose a new password
66 def lost_password
66 def lost_password
67 redirect_to(home_url) && return unless Setting.lost_password?
67 redirect_to(home_url) && return unless Setting.lost_password?
68 if params[:token]
68 if params[:token]
69 @token = Token.find_by_action_and_value("recovery", params[:token])
69 @token = Token.find_by_action_and_value("recovery", params[:token])
70 redirect_to(home_url) && return unless @token and !@token.expired?
70 redirect_to(home_url) && return unless @token and !@token.expired?
71 @user = @token.user
71 @user = @token.user
72 if request.post?
72 if request.post?
73 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
73 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
74 if @user.save
74 if @user.save
75 @token.destroy
75 @token.destroy
76 flash[:notice] = l(:notice_account_password_updated)
76 flash[:notice] = l(:notice_account_password_updated)
77 redirect_to :action => 'login'
77 redirect_to :action => 'login'
78 return
78 return
79 end
79 end
80 end
80 end
81 render :template => "account/password_recovery"
81 render :template => "account/password_recovery"
82 return
82 return
83 else
83 else
84 if request.post?
84 if request.post?
85 user = User.find_by_mail(params[:mail])
85 user = User.find_by_mail(params[:mail])
86 # user not found in db
86 # user not found in db
87 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
87 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
88 # user uses an external authentification
88 # user uses an external authentification
89 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
89 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
90 # create a new token for password recovery
90 # create a new token for password recovery
91 token = Token.new(:user => user, :action => "recovery")
91 token = Token.new(:user => user, :action => "recovery")
92 if token.save
92 if token.save
93 Mailer.deliver_lost_password(token)
93 Mailer.deliver_lost_password(token)
94 flash[:notice] = l(:notice_account_lost_email_sent)
94 flash[:notice] = l(:notice_account_lost_email_sent)
95 redirect_to :action => 'login'
95 redirect_to :action => 'login'
96 return
96 return
97 end
97 end
98 end
98 end
99 end
99 end
100 end
100 end
101
101
102 # User self-registration
102 # User self-registration
103 def register
103 def register
104 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
104 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
105 if request.get?
105 if request.get?
106 session[:auth_source_registration] = nil
106 session[:auth_source_registration] = nil
107 @user = User.new(:language => Setting.default_language)
107 @user = User.new(:language => Setting.default_language)
108 else
108 else
109 @user = User.new(params[:user])
109 @user = User.new(params[:user])
110 @user.admin = false
110 @user.admin = false
111 @user.status = User::STATUS_REGISTERED
111 @user.status = User::STATUS_REGISTERED
112 if session[:auth_source_registration]
112 if session[:auth_source_registration]
113 @user.status = User::STATUS_ACTIVE
113 @user.status = User::STATUS_ACTIVE
114 @user.login = session[:auth_source_registration][:login]
114 @user.login = session[:auth_source_registration][:login]
115 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
115 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
116 if @user.save
116 if @user.save
117 session[:auth_source_registration] = nil
117 session[:auth_source_registration] = nil
118 self.logged_user = @user
118 self.logged_user = @user
119 flash[:notice] = l(:notice_account_activated)
119 flash[:notice] = l(:notice_account_activated)
120 redirect_to :controller => 'my', :action => 'account'
120 redirect_to :controller => 'my', :action => 'account'
121 end
121 end
122 else
122 else
123 @user.login = params[:user][:login]
123 @user.login = params[:user][:login]
124 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
124 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
125 case Setting.self_registration
125 case Setting.self_registration
126 when '1'
126 when '1'
127 # Email activation
127 # Email activation
128 token = Token.new(:user => @user, :action => "register")
128 token = Token.new(:user => @user, :action => "register")
129 if @user.save and token.save
129 if @user.save and token.save
130 Mailer.deliver_register(token)
130 Mailer.deliver_register(token)
131 flash[:notice] = l(:notice_account_register_done)
131 flash[:notice] = l(:notice_account_register_done)
132 redirect_to :action => 'login'
132 redirect_to :action => 'login'
133 end
133 end
134 when '3'
134 when '3'
135 # Automatic activation
135 # Automatic activation
136 @user.status = User::STATUS_ACTIVE
136 @user.status = User::STATUS_ACTIVE
137 if @user.save
137 if @user.save
138 self.logged_user = @user
138 self.logged_user = @user
139 flash[:notice] = l(:notice_account_activated)
139 flash[:notice] = l(:notice_account_activated)
140 redirect_to :controller => 'my', :action => 'account'
140 redirect_to :controller => 'my', :action => 'account'
141 end
141 end
142 else
142 else
143 # Manual activation by the administrator
143 # Manual activation by the administrator
144 if @user.save
144 if @user.save
145 # Sends an email to the administrators
145 # Sends an email to the administrators
146 Mailer.deliver_account_activation_request(@user)
146 Mailer.deliver_account_activation_request(@user)
147 flash[:notice] = l(:notice_account_pending)
147 flash[:notice] = l(:notice_account_pending)
148 redirect_to :action => 'login'
148 redirect_to :action => 'login'
149 end
149 end
150 end
150 end
151 end
151 end
152 end
152 end
153 end
153 end
154
154
155 # Token based account activation
155 # Token based account activation
156 def activate
156 def activate
157 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
157 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
158 token = Token.find_by_action_and_value('register', params[:token])
158 token = Token.find_by_action_and_value('register', params[:token])
159 redirect_to(home_url) && return unless token and !token.expired?
159 redirect_to(home_url) && return unless token and !token.expired?
160 user = token.user
160 user = token.user
161 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
161 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
162 user.status = User::STATUS_ACTIVE
162 user.status = User::STATUS_ACTIVE
163 if user.save
163 if user.save
164 token.destroy
164 token.destroy
165 flash[:notice] = l(:notice_account_activated)
165 flash[:notice] = l(:notice_account_activated)
166 end
166 end
167 redirect_to :action => 'login'
167 redirect_to :action => 'login'
168 end
168 end
169
169
170 private
170 private
171 def logged_user=(user)
171 def logged_user=(user)
172 if user && user.is_a?(User)
172 if user && user.is_a?(User)
173 User.current = user
173 User.current = user
174 session[:user_id] = user.id
174 session[:user_id] = user.id
175 else
175 else
176 User.current = User.anonymous
176 User.current = User.anonymous
177 session[:user_id] = nil
177 session[:user_id] = nil
178 end
178 end
179 end
179 end
180
180
181 def password_authentication
181 def password_authentication
182 user = User.try_to_login(params[:username], params[:password])
182 user = User.try_to_login(params[:username], params[:password])
183 if user.nil?
183 if user.nil?
184 # Invalid credentials
184 # Invalid credentials
185 flash.now[:error] = l(:notice_account_invalid_creditentials)
185 flash.now[:error] = l(:notice_account_invalid_creditentials)
186 elsif user.new_record?
186 elsif user.new_record?
187 # Onthefly creation failed, display the registration form to fill/fix attributes
187 # Onthefly creation failed, display the registration form to fill/fix attributes
188 @user = user
188 @user = user
189 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
189 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
190 render :action => 'register'
190 render :action => 'register'
191 else
191 else
192 # Valid user
192 # Valid user
193 successful_authentication(user)
193 successful_authentication(user)
194 end
194 end
195 end
195 end
196
196
197
197
198 def open_id_authenticate(openid_url)
198 def open_id_authenticate(openid_url)
199 user = nil
200 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
199 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
201 if result.successful?
200 if result.successful?
202 user = User.find_or_initialize_by_identity_url(identity_url)
201 user = User.find_or_initialize_by_identity_url(identity_url)
203 if user.new_record?
202 if user.new_record?
204 # Create on the fly
203 # Create on the fly
205 # TODO: name
206 user.login = registration['nickname']
204 user.login = registration['nickname']
207 user.mail = registration['email']
205 user.mail = registration['email']
208 user.save
206 user.firstname, user.lastname = registration['fullname'].split(' ')
209 end
207 user.random_password
210
208 if user.save
211 user.reload
209 successful_authentication(user)
212 if user.new_record?
210 else
213 # Onthefly creation failed, display the registration form to fill/fix attributes
211 # Onthefly creation failed, display the registration form to fill/fix attributes
214 @user = user
212 @user = user
215 session[:auth_source_registration] = {:login => user.login, :identity_url => identity_url }
213 session[:auth_source_registration] = {:login => user.login, :identity_url => identity_url }
216 render :action => 'register'
214 render :action => 'register'
215 end
217 else
216 else
217 # Existing record
218 successful_authentication(user)
218 successful_authentication(user)
219 end
219 end
220 end
220 end
221 end
221 end
222 end
222 end
223
223
224 def successful_authentication(user)
224 def successful_authentication(user)
225 # Valid user
225 # Valid user
226 self.logged_user = user
226 self.logged_user = user
227 # generate a key and set cookie if autologin
227 # generate a key and set cookie if autologin
228 if params[:autologin] && Setting.autologin?
228 if params[:autologin] && Setting.autologin?
229 token = Token.create(:user => user, :action => 'autologin')
229 token = Token.create(:user => user, :action => 'autologin')
230 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
230 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
231 end
231 end
232 redirect_back_or_default :controller => 'my', :action => 'page'
232 redirect_back_or_default :controller => 'my', :action => 'page'
233 end
233 end
234
234
235 end
235 end
@@ -1,294 +1,306
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 < ActiveRecord::Base
20 class User < ActiveRecord::Base
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_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
37 has_many :members, :dependent => :delete_all
37 has_many :members, :dependent => :delete_all
38 has_many :projects, :through => :memberships
38 has_many :projects, :through => :memberships
39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
40 has_many :changesets, :dependent => :nullify
40 has_many :changesets, :dependent => :nullify
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
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
53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
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? }
56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
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_length_of :password, :minimum => 4, :allow_nil => true
65 validates_length_of :password, :minimum => 4, :allow_nil => true
66 validates_confirmation_of :password, :allow_nil => true
66 validates_confirmation_of :password, :allow_nil => true
67
67
68 def before_create
68 def before_create
69 self.mail_notification = false
69 self.mail_notification = false
70 true
70 true
71 end
71 end
72
72
73 def before_save
73 def before_save
74 # update hashed_password if password was set
74 # update hashed_password if password was set
75 self.hashed_password = User.hash_password(self.password) if self.password
75 self.hashed_password = User.hash_password(self.password) if self.password
76 end
76 end
77
77
78 def reload(*args)
78 def reload(*args)
79 @name = nil
79 @name = nil
80 super
80 super
81 end
81 end
82
82
83 # Returns the user that matches provided login and password, or nil
83 # Returns the user that matches provided login and password, or nil
84 def self.try_to_login(login, password)
84 def self.try_to_login(login, password)
85 # Make sure no one can sign in with an empty password
85 # Make sure no one can sign in with an empty password
86 return nil if password.to_s.empty?
86 return nil if password.to_s.empty?
87 user = find(:first, :conditions => ["login=?", login])
87 user = find(:first, :conditions => ["login=?", login])
88 if user
88 if user
89 # user is already in local database
89 # user is already in local database
90 return nil if !user.active?
90 return nil if !user.active?
91 if user.auth_source
91 if user.auth_source
92 # user has an external authentication method
92 # user has an external authentication method
93 return nil unless user.auth_source.authenticate(login, password)
93 return nil unless user.auth_source.authenticate(login, password)
94 else
94 else
95 # authentication with local password
95 # authentication with local password
96 return nil unless User.hash_password(password) == user.hashed_password
96 return nil unless User.hash_password(password) == user.hashed_password
97 end
97 end
98 else
98 else
99 # user is not yet registered, try to authenticate with available sources
99 # user is not yet registered, try to authenticate with available sources
100 attrs = AuthSource.authenticate(login, password)
100 attrs = AuthSource.authenticate(login, password)
101 if attrs
101 if attrs
102 user = new(*attrs)
102 user = new(*attrs)
103 user.login = login
103 user.login = login
104 user.language = Setting.default_language
104 user.language = Setting.default_language
105 if user.save
105 if user.save
106 user.reload
106 user.reload
107 logger.info("User '#{user.login}' created from the LDAP") if logger
107 logger.info("User '#{user.login}' created from the LDAP") if logger
108 end
108 end
109 end
109 end
110 end
110 end
111 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
111 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
112 user
112 user
113 rescue => text
113 rescue => text
114 raise text
114 raise text
115 end
115 end
116
116
117 # Return user's full name for display
117 # Return user's full name for display
118 def name(formatter = nil)
118 def name(formatter = nil)
119 if formatter
119 if formatter
120 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
120 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
121 else
121 else
122 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
122 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
123 end
123 end
124 end
124 end
125
125
126 def active?
126 def active?
127 self.status == STATUS_ACTIVE
127 self.status == STATUS_ACTIVE
128 end
128 end
129
129
130 def registered?
130 def registered?
131 self.status == STATUS_REGISTERED
131 self.status == STATUS_REGISTERED
132 end
132 end
133
133
134 def locked?
134 def locked?
135 self.status == STATUS_LOCKED
135 self.status == STATUS_LOCKED
136 end
136 end
137
137
138 def check_password?(clear_password)
138 def check_password?(clear_password)
139 User.hash_password(clear_password) == self.hashed_password
139 User.hash_password(clear_password) == self.hashed_password
140 end
140 end
141
142 # Generate and set a random password. Useful for automated user creation
143 # Based on Token#generate_token_value
144 #
145 def random_password
146 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
147 password = ''
148 40.times { |i| password << chars[rand(chars.size-1)] }
149 self.password = password
150 self.password_confirmation = password
151 self
152 end
141
153
142 def pref
154 def pref
143 self.preference ||= UserPreference.new(:user => self)
155 self.preference ||= UserPreference.new(:user => self)
144 end
156 end
145
157
146 def time_zone
158 def time_zone
147 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
159 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
148 end
160 end
149
161
150 def wants_comments_in_reverse_order?
162 def wants_comments_in_reverse_order?
151 self.pref[:comments_sorting] == 'desc'
163 self.pref[:comments_sorting] == 'desc'
152 end
164 end
153
165
154 # Return user's RSS key (a 40 chars long string), used to access feeds
166 # Return user's RSS key (a 40 chars long string), used to access feeds
155 def rss_key
167 def rss_key
156 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
168 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
157 token.value
169 token.value
158 end
170 end
159
171
160 # Return an array of project ids for which the user has explicitly turned mail notifications on
172 # Return an array of project ids for which the user has explicitly turned mail notifications on
161 def notified_projects_ids
173 def notified_projects_ids
162 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
174 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
163 end
175 end
164
176
165 def notified_project_ids=(ids)
177 def notified_project_ids=(ids)
166 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
178 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
167 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
179 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
168 @notified_projects_ids = nil
180 @notified_projects_ids = nil
169 notified_projects_ids
181 notified_projects_ids
170 end
182 end
171
183
172 def self.find_by_rss_key(key)
184 def self.find_by_rss_key(key)
173 token = Token.find_by_value(key)
185 token = Token.find_by_value(key)
174 token && token.user.active? ? token.user : nil
186 token && token.user.active? ? token.user : nil
175 end
187 end
176
188
177 def self.find_by_autologin_key(key)
189 def self.find_by_autologin_key(key)
178 token = Token.find_by_action_and_value('autologin', key)
190 token = Token.find_by_action_and_value('autologin', key)
179 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
191 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
180 end
192 end
181
193
182 # Makes find_by_mail case-insensitive
194 # Makes find_by_mail case-insensitive
183 def self.find_by_mail(mail)
195 def self.find_by_mail(mail)
184 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
196 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
185 end
197 end
186
198
187 # Sort users by their display names
199 # Sort users by their display names
188 def <=>(user)
200 def <=>(user)
189 self.to_s.downcase <=> user.to_s.downcase
201 self.to_s.downcase <=> user.to_s.downcase
190 end
202 end
191
203
192 def to_s
204 def to_s
193 name
205 name
194 end
206 end
195
207
196 def logged?
208 def logged?
197 true
209 true
198 end
210 end
199
211
200 def anonymous?
212 def anonymous?
201 !logged?
213 !logged?
202 end
214 end
203
215
204 # Return user's role for project
216 # Return user's role for project
205 def role_for_project(project)
217 def role_for_project(project)
206 # No role on archived projects
218 # No role on archived projects
207 return nil unless project && project.active?
219 return nil unless project && project.active?
208 if logged?
220 if logged?
209 # Find project membership
221 # Find project membership
210 membership = memberships.detect {|m| m.project_id == project.id}
222 membership = memberships.detect {|m| m.project_id == project.id}
211 if membership
223 if membership
212 membership.role
224 membership.role
213 else
225 else
214 @role_non_member ||= Role.non_member
226 @role_non_member ||= Role.non_member
215 end
227 end
216 else
228 else
217 @role_anonymous ||= Role.anonymous
229 @role_anonymous ||= Role.anonymous
218 end
230 end
219 end
231 end
220
232
221 # Return true if the user is a member of project
233 # Return true if the user is a member of project
222 def member_of?(project)
234 def member_of?(project)
223 role_for_project(project).member?
235 role_for_project(project).member?
224 end
236 end
225
237
226 # Return true if the user is allowed to do the specified action on project
238 # Return true if the user is allowed to do the specified action on project
227 # action can be:
239 # action can be:
228 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
240 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
229 # * a permission Symbol (eg. :edit_project)
241 # * a permission Symbol (eg. :edit_project)
230 def allowed_to?(action, project, options={})
242 def allowed_to?(action, project, options={})
231 if project
243 if project
232 # No action allowed on archived projects
244 # No action allowed on archived projects
233 return false unless project.active?
245 return false unless project.active?
234 # No action allowed on disabled modules
246 # No action allowed on disabled modules
235 return false unless project.allows_to?(action)
247 return false unless project.allows_to?(action)
236 # Admin users are authorized for anything else
248 # Admin users are authorized for anything else
237 return true if admin?
249 return true if admin?
238
250
239 role = role_for_project(project)
251 role = role_for_project(project)
240 return false unless role
252 return false unless role
241 role.allowed_to?(action) && (project.is_public? || role.member?)
253 role.allowed_to?(action) && (project.is_public? || role.member?)
242
254
243 elsif options[:global]
255 elsif options[:global]
244 # authorize if user has at least one role that has this permission
256 # authorize if user has at least one role that has this permission
245 roles = memberships.collect {|m| m.role}.uniq
257 roles = memberships.collect {|m| m.role}.uniq
246 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
258 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
247 else
259 else
248 false
260 false
249 end
261 end
250 end
262 end
251
263
252 def self.current=(user)
264 def self.current=(user)
253 @current_user = user
265 @current_user = user
254 end
266 end
255
267
256 def self.current
268 def self.current
257 @current_user ||= User.anonymous
269 @current_user ||= User.anonymous
258 end
270 end
259
271
260 def self.anonymous
272 def self.anonymous
261 anonymous_user = AnonymousUser.find(:first)
273 anonymous_user = AnonymousUser.find(:first)
262 if anonymous_user.nil?
274 if anonymous_user.nil?
263 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
275 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
264 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
276 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
265 end
277 end
266 anonymous_user
278 anonymous_user
267 end
279 end
268
280
269 private
281 private
270 # Return password digest
282 # Return password digest
271 def self.hash_password(clear_password)
283 def self.hash_password(clear_password)
272 Digest::SHA1.hexdigest(clear_password || "")
284 Digest::SHA1.hexdigest(clear_password || "")
273 end
285 end
274 end
286 end
275
287
276 class AnonymousUser < User
288 class AnonymousUser < User
277
289
278 def validate_on_create
290 def validate_on_create
279 # There should be only one AnonymousUser in the database
291 # There should be only one AnonymousUser in the database
280 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
292 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
281 end
293 end
282
294
283 def available_custom_fields
295 def available_custom_fields
284 []
296 []
285 end
297 end
286
298
287 # Overrides a few properties
299 # Overrides a few properties
288 def logged?; false end
300 def logged?; false end
289 def admin; false end
301 def admin; false end
290 def name; 'Anonymous' end
302 def name; 'Anonymous' end
291 def mail; nil end
303 def mail; nil end
292 def time_zone; nil end
304 def time_zone; nil end
293 def rss_key; nil end
305 def rss_key; nil end
294 end
306 end
@@ -1,180 +1,187
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class UserTest < Test::Unit::TestCase
20 class UserTest < Test::Unit::TestCase
21 fixtures :users, :members, :projects
21 fixtures :users, :members, :projects
22
22
23 def setup
23 def setup
24 @admin = User.find(1)
24 @admin = User.find(1)
25 @jsmith = User.find(2)
25 @jsmith = User.find(2)
26 @dlopper = User.find(3)
26 @dlopper = User.find(3)
27 end
27 end
28
28
29 def test_truth
29 def test_truth
30 assert_kind_of User, @jsmith
30 assert_kind_of User, @jsmith
31 end
31 end
32
32
33 def test_create
33 def test_create
34 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
34 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
35
35
36 user.login = "jsmith"
36 user.login = "jsmith"
37 user.password, user.password_confirmation = "password", "password"
37 user.password, user.password_confirmation = "password", "password"
38 # login uniqueness
38 # login uniqueness
39 assert !user.save
39 assert !user.save
40 assert_equal 1, user.errors.count
40 assert_equal 1, user.errors.count
41
41
42 user.login = "newuser"
42 user.login = "newuser"
43 user.password, user.password_confirmation = "passwd", "password"
43 user.password, user.password_confirmation = "passwd", "password"
44 # password confirmation
44 # password confirmation
45 assert !user.save
45 assert !user.save
46 assert_equal 1, user.errors.count
46 assert_equal 1, user.errors.count
47
47
48 user.password, user.password_confirmation = "password", "password"
48 user.password, user.password_confirmation = "password", "password"
49 assert user.save
49 assert user.save
50 end
50 end
51
51
52 def test_mail_uniqueness_should_not_be_case_sensitive
52 def test_mail_uniqueness_should_not_be_case_sensitive
53 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
53 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
54 u.login = 'newuser1'
54 u.login = 'newuser1'
55 u.password, u.password_confirmation = "password", "password"
55 u.password, u.password_confirmation = "password", "password"
56 assert u.save
56 assert u.save
57
57
58 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
58 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
59 u.login = 'newuser2'
59 u.login = 'newuser2'
60 u.password, u.password_confirmation = "password", "password"
60 u.password, u.password_confirmation = "password", "password"
61 assert !u.save
61 assert !u.save
62 assert_equal 'activerecord_error_taken', u.errors.on(:mail)
62 assert_equal 'activerecord_error_taken', u.errors.on(:mail)
63 end
63 end
64
64
65 def test_update
65 def test_update
66 assert_equal "admin", @admin.login
66 assert_equal "admin", @admin.login
67 @admin.login = "john"
67 @admin.login = "john"
68 assert @admin.save, @admin.errors.full_messages.join("; ")
68 assert @admin.save, @admin.errors.full_messages.join("; ")
69 @admin.reload
69 @admin.reload
70 assert_equal "john", @admin.login
70 assert_equal "john", @admin.login
71 end
71 end
72
72
73 def test_destroy
73 def test_destroy
74 User.find(2).destroy
74 User.find(2).destroy
75 assert_nil User.find_by_id(2)
75 assert_nil User.find_by_id(2)
76 assert Member.find_all_by_user_id(2).empty?
76 assert Member.find_all_by_user_id(2).empty?
77 end
77 end
78
78
79 def test_validate
79 def test_validate
80 @admin.login = ""
80 @admin.login = ""
81 assert !@admin.save
81 assert !@admin.save
82 assert_equal 1, @admin.errors.count
82 assert_equal 1, @admin.errors.count
83 end
83 end
84
84
85 def test_password
85 def test_password
86 user = User.try_to_login("admin", "admin")
86 user = User.try_to_login("admin", "admin")
87 assert_kind_of User, user
87 assert_kind_of User, user
88 assert_equal "admin", user.login
88 assert_equal "admin", user.login
89 user.password = "hello"
89 user.password = "hello"
90 assert user.save
90 assert user.save
91
91
92 user = User.try_to_login("admin", "hello")
92 user = User.try_to_login("admin", "hello")
93 assert_kind_of User, user
93 assert_kind_of User, user
94 assert_equal "admin", user.login
94 assert_equal "admin", user.login
95 assert_equal User.hash_password("hello"), user.hashed_password
95 assert_equal User.hash_password("hello"), user.hashed_password
96 end
96 end
97
97
98 def test_name_format
98 def test_name_format
99 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
99 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
100 Setting.user_format = :firstname_lastname
100 Setting.user_format = :firstname_lastname
101 assert_equal 'John Smith', @jsmith.reload.name
101 assert_equal 'John Smith', @jsmith.reload.name
102 Setting.user_format = :username
102 Setting.user_format = :username
103 assert_equal 'jsmith', @jsmith.reload.name
103 assert_equal 'jsmith', @jsmith.reload.name
104 end
104 end
105
105
106 def test_lock
106 def test_lock
107 user = User.try_to_login("jsmith", "jsmith")
107 user = User.try_to_login("jsmith", "jsmith")
108 assert_equal @jsmith, user
108 assert_equal @jsmith, user
109
109
110 @jsmith.status = User::STATUS_LOCKED
110 @jsmith.status = User::STATUS_LOCKED
111 assert @jsmith.save
111 assert @jsmith.save
112
112
113 user = User.try_to_login("jsmith", "jsmith")
113 user = User.try_to_login("jsmith", "jsmith")
114 assert_equal nil, user
114 assert_equal nil, user
115 end
115 end
116
116
117 def test_create_anonymous
117 def test_create_anonymous
118 AnonymousUser.delete_all
118 AnonymousUser.delete_all
119 anon = User.anonymous
119 anon = User.anonymous
120 assert !anon.new_record?
120 assert !anon.new_record?
121 assert_kind_of AnonymousUser, anon
121 assert_kind_of AnonymousUser, anon
122 end
122 end
123
123
124 def test_rss_key
124 def test_rss_key
125 assert_nil @jsmith.rss_token
125 assert_nil @jsmith.rss_token
126 key = @jsmith.rss_key
126 key = @jsmith.rss_key
127 assert_equal 40, key.length
127 assert_equal 40, key.length
128
128
129 @jsmith.reload
129 @jsmith.reload
130 assert_equal key, @jsmith.rss_key
130 assert_equal key, @jsmith.rss_key
131 end
131 end
132
132
133 def test_role_for_project
133 def test_role_for_project
134 # user with a role
134 # user with a role
135 role = @jsmith.role_for_project(Project.find(1))
135 role = @jsmith.role_for_project(Project.find(1))
136 assert_kind_of Role, role
136 assert_kind_of Role, role
137 assert_equal "Manager", role.name
137 assert_equal "Manager", role.name
138
138
139 # user with no role
139 # user with no role
140 assert !@dlopper.role_for_project(Project.find(2)).member?
140 assert !@dlopper.role_for_project(Project.find(2)).member?
141 end
141 end
142
142
143 def test_mail_notification_all
143 def test_mail_notification_all
144 @jsmith.mail_notification = true
144 @jsmith.mail_notification = true
145 @jsmith.notified_project_ids = []
145 @jsmith.notified_project_ids = []
146 @jsmith.save
146 @jsmith.save
147 @jsmith.reload
147 @jsmith.reload
148 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
148 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
149 end
149 end
150
150
151 def test_mail_notification_selected
151 def test_mail_notification_selected
152 @jsmith.mail_notification = false
152 @jsmith.mail_notification = false
153 @jsmith.notified_project_ids = [1]
153 @jsmith.notified_project_ids = [1]
154 @jsmith.save
154 @jsmith.save
155 @jsmith.reload
155 @jsmith.reload
156 assert Project.find(1).recipients.include?(@jsmith.mail)
156 assert Project.find(1).recipients.include?(@jsmith.mail)
157 end
157 end
158
158
159 def test_mail_notification_none
159 def test_mail_notification_none
160 @jsmith.mail_notification = false
160 @jsmith.mail_notification = false
161 @jsmith.notified_project_ids = []
161 @jsmith.notified_project_ids = []
162 @jsmith.save
162 @jsmith.save
163 @jsmith.reload
163 @jsmith.reload
164 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
164 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
165 end
165 end
166
166
167 def test_comments_sorting_preference
167 def test_comments_sorting_preference
168 assert !@jsmith.wants_comments_in_reverse_order?
168 assert !@jsmith.wants_comments_in_reverse_order?
169 @jsmith.pref.comments_sorting = 'asc'
169 @jsmith.pref.comments_sorting = 'asc'
170 assert !@jsmith.wants_comments_in_reverse_order?
170 assert !@jsmith.wants_comments_in_reverse_order?
171 @jsmith.pref.comments_sorting = 'desc'
171 @jsmith.pref.comments_sorting = 'desc'
172 assert @jsmith.wants_comments_in_reverse_order?
172 assert @jsmith.wants_comments_in_reverse_order?
173 end
173 end
174
174
175 def test_find_by_mail_should_be_case_insensitive
175 def test_find_by_mail_should_be_case_insensitive
176 u = User.find_by_mail('JSmith@somenet.foo')
176 u = User.find_by_mail('JSmith@somenet.foo')
177 assert_not_nil u
177 assert_not_nil u
178 assert_equal 'jsmith@somenet.foo', u.mail
178 assert_equal 'jsmith@somenet.foo', u.mail
179 end
179 end
180
181 def test_random_password
182 u = User.new
183 u.random_password
184 assert !u.password.blank?
185 assert !u.password_confirmation.blank?
186 end
180 end
187 end
General Comments 0
You need to be logged in to leave comments. Login now