##// END OF EJS Templates
Fixed: When logging in via an autologin cookie the user's last_login_on should be updated (#2820)....
Jean-Philippe Lang -
r2460:21eb3c089d08
parent child
Show More
@@ -1,286 +1,277
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class AccountController < ApplicationController
19 19 helper :custom_fields
20 20 include CustomFieldsHelper
21 21
22 22 # prevents login action to be filtered by check_if_login_required application scope filter
23 23 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
24 24
25 25 # Show user's account
26 26 def show
27 27 @user = User.active.find(params[:id])
28 28 @custom_values = @user.custom_values
29 29
30 30 # show only public projects and private projects that the logged in user is also a member of
31 31 @memberships = @user.memberships.select do |membership|
32 32 membership.project.is_public? || (User.current.member_of?(membership.project))
33 33 end
34 34
35 35 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
36 36 @events_by_day = events.group_by(&:event_date)
37 37
38 38 rescue ActiveRecord::RecordNotFound
39 39 render_404
40 40 end
41 41
42 42 # Login request and validation
43 43 def login
44 44 if request.get?
45 45 # Logout user
46 46 self.logged_user = nil
47 47 else
48 48 # Authenticate user
49 49 if Setting.openid? && using_open_id?
50 50 open_id_authenticate(params[:openid_url])
51 51 else
52 52 password_authentication
53 53 end
54 54 end
55 55 end
56 56
57 57 # Log out current user and redirect to welcome page
58 58 def logout
59 59 cookies.delete :autologin
60 60 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
61 61 self.logged_user = nil
62 62 redirect_to home_url
63 63 end
64 64
65 65 # Enable user to choose a new password
66 66 def lost_password
67 67 redirect_to(home_url) && return unless Setting.lost_password?
68 68 if params[:token]
69 69 @token = Token.find_by_action_and_value("recovery", params[:token])
70 70 redirect_to(home_url) && return unless @token and !@token.expired?
71 71 @user = @token.user
72 72 if request.post?
73 73 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
74 74 if @user.save
75 75 @token.destroy
76 76 flash[:notice] = l(:notice_account_password_updated)
77 77 redirect_to :action => 'login'
78 78 return
79 79 end
80 80 end
81 81 render :template => "account/password_recovery"
82 82 return
83 83 else
84 84 if request.post?
85 85 user = User.find_by_mail(params[:mail])
86 86 # user not found in db
87 87 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
88 88 # user uses an external authentification
89 89 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
90 90 # create a new token for password recovery
91 91 token = Token.new(:user => user, :action => "recovery")
92 92 if token.save
93 93 Mailer.deliver_lost_password(token)
94 94 flash[:notice] = l(:notice_account_lost_email_sent)
95 95 redirect_to :action => 'login'
96 96 return
97 97 end
98 98 end
99 99 end
100 100 end
101 101
102 102 # User self-registration
103 103 def register
104 104 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
105 105 if request.get?
106 106 session[:auth_source_registration] = nil
107 107 @user = User.new(:language => Setting.default_language)
108 108 else
109 109 @user = User.new(params[:user])
110 110 @user.admin = false
111 111 @user.status = User::STATUS_REGISTERED
112 112 if session[:auth_source_registration]
113 113 @user.status = User::STATUS_ACTIVE
114 114 @user.login = session[:auth_source_registration][:login]
115 115 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
116 116 if @user.save
117 117 session[:auth_source_registration] = nil
118 118 self.logged_user = @user
119 119 flash[:notice] = l(:notice_account_activated)
120 120 redirect_to :controller => 'my', :action => 'account'
121 121 end
122 122 else
123 123 @user.login = params[:user][:login]
124 124 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
125 125
126 126 case Setting.self_registration
127 127 when '1'
128 128 register_by_email_activation(@user)
129 129 when '3'
130 130 register_automatically(@user)
131 131 else
132 132 register_manually_by_administrator(@user)
133 133 end
134 134 end
135 135 end
136 136 end
137 137
138 138 # Token based account activation
139 139 def activate
140 140 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
141 141 token = Token.find_by_action_and_value('register', params[:token])
142 142 redirect_to(home_url) && return unless token and !token.expired?
143 143 user = token.user
144 144 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
145 145 user.status = User::STATUS_ACTIVE
146 146 if user.save
147 147 token.destroy
148 148 flash[:notice] = l(:notice_account_activated)
149 149 end
150 150 redirect_to :action => 'login'
151 151 end
152 152
153 private
154 def logged_user=(user)
155 if user && user.is_a?(User)
156 User.current = user
157 session[:user_id] = user.id
158 else
159 User.current = User.anonymous
160 session[:user_id] = nil
161 end
162 end
163
153 private
154
164 155 def password_authentication
165 156 user = User.try_to_login(params[:username], params[:password])
166 157 if user.nil?
167 158 # Invalid credentials
168 159 flash.now[:error] = l(:notice_account_invalid_creditentials)
169 160 elsif user.new_record?
170 161 # Onthefly creation failed, display the registration form to fill/fix attributes
171 162 @user = user
172 163 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
173 164 render :action => 'register'
174 165 else
175 166 # Valid user
176 167 successful_authentication(user)
177 168 end
178 169 end
179 170
180 171
181 172 def open_id_authenticate(openid_url)
182 173 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
183 174 if result.successful?
184 175 user = User.find_or_initialize_by_identity_url(identity_url)
185 176 if user.new_record?
186 177 # Self-registration off
187 178 redirect_to(home_url) && return unless Setting.self_registration?
188 179
189 180 # Create on the fly
190 181 user.login = registration['nickname'] unless registration['nickname'].nil?
191 182 user.mail = registration['email'] unless registration['email'].nil?
192 183 user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
193 184 user.random_password
194 185 user.status = User::STATUS_REGISTERED
195 186
196 187 case Setting.self_registration
197 188 when '1'
198 189 register_by_email_activation(user) do
199 190 onthefly_creation_failed(user)
200 191 end
201 192 when '3'
202 193 register_automatically(user) do
203 194 onthefly_creation_failed(user)
204 195 end
205 196 else
206 197 register_manually_by_administrator(user) do
207 198 onthefly_creation_failed(user)
208 199 end
209 200 end
210 201 else
211 202 # Existing record
212 203 if user.active?
213 204 successful_authentication(user)
214 205 else
215 206 account_pending
216 207 end
217 208 end
218 209 end
219 210 end
220 211 end
221 212
222 213 def successful_authentication(user)
223 214 # Valid user
224 215 self.logged_user = user
225 216 # generate a key and set cookie if autologin
226 217 if params[:autologin] && Setting.autologin?
227 218 token = Token.create(:user => user, :action => 'autologin')
228 219 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
229 220 end
230 221 redirect_back_or_default :controller => 'my', :action => 'page'
231 222 end
232 223
233 224 # Onthefly creation failed, display the registration form to fill/fix attributes
234 225 def onthefly_creation_failed(user, auth_source_options = { })
235 226 @user = user
236 227 session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
237 228 render :action => 'register'
238 229 end
239 230
240 231 # Register a user for email activation.
241 232 #
242 233 # Pass a block for behavior when a user fails to save
243 234 def register_by_email_activation(user, &block)
244 235 token = Token.new(:user => user, :action => "register")
245 236 if user.save and token.save
246 237 Mailer.deliver_register(token)
247 238 flash[:notice] = l(:notice_account_register_done)
248 239 redirect_to :action => 'login'
249 240 else
250 241 yield if block_given?
251 242 end
252 243 end
253 244
254 245 # Automatically register a user
255 246 #
256 247 # Pass a block for behavior when a user fails to save
257 248 def register_automatically(user, &block)
258 249 # Automatic activation
259 250 user.status = User::STATUS_ACTIVE
260 251 if user.save
261 252 self.logged_user = user
262 253 flash[:notice] = l(:notice_account_activated)
263 254 redirect_to :controller => 'my', :action => 'account'
264 255 else
265 256 yield if block_given?
266 257 end
267 258 end
268 259
269 260 # Manual activation by the administrator
270 261 #
271 262 # Pass a block for behavior when a user fails to save
272 263 def register_manually_by_administrator(user, &block)
273 264 if user.save
274 265 # Sends an email to the administrators
275 266 Mailer.deliver_account_activation_request(user)
276 267 account_pending
277 268 else
278 269 yield if block_given?
279 270 end
280 271 end
281 272
282 273 def account_pending
283 274 flash[:notice] = l(:notice_account_pending)
284 275 redirect_to :action => 'login'
285 276 end
286 277 end
@@ -1,241 +1,252
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'uri'
19 19 require 'cgi'
20 20
21 21 class ApplicationController < ActionController::Base
22 22 include Redmine::I18n
23 23
24 24 # In case the cookie store secret changes
25 25 rescue_from CGI::Session::CookieStore::TamperedWithCookie do |exception|
26 26 render :text => 'Your session was invalid and has been reset. Please, reload this page.', :status => 500
27 27 end
28 28
29 29 layout 'base'
30 30
31 31 before_filter :user_setup, :check_if_login_required, :set_localization
32 32 filter_parameter_logging :password
33 33
34 34 include Redmine::MenuManager::MenuController
35 35 helper Redmine::MenuManager::MenuHelper
36 36
37 37 REDMINE_SUPPORTED_SCM.each do |scm|
38 38 require_dependency "repository/#{scm.underscore}"
39 39 end
40 40
41 41 def current_role
42 42 @current_role ||= User.current.role_for_project(@project)
43 43 end
44 44
45 45 def user_setup
46 46 # Check the settings cache for each request
47 47 Setting.check_cache
48 48 # Find the current user
49 User.current = find_current_user
49 self.logged_user = find_current_user
50 50 end
51 51
52 52 # Returns the current user or nil if no user is logged in
53 53 def find_current_user
54 54 if session[:user_id]
55 55 # existing session
56 56 (User.active.find(session[:user_id]) rescue nil)
57 57 elsif cookies[:autologin] && Setting.autologin?
58 58 # auto-login feature
59 User.find_by_autologin_key(cookies[:autologin])
59 User.try_to_autologin(cookies[:autologin])
60 60 elsif params[:key] && accept_key_auth_actions.include?(params[:action])
61 61 # RSS key authentication
62 62 User.find_by_rss_key(params[:key])
63 63 end
64 64 end
65 65
66 # Sets the logged in user
67 def logged_user=(user)
68 if user && user.is_a?(User)
69 User.current = user
70 session[:user_id] = user.id
71 else
72 User.current = User.anonymous
73 session[:user_id] = nil
74 end
75 end
76
66 77 # check if login is globally required to access the application
67 78 def check_if_login_required
68 79 # no check needed if user is already logged in
69 80 return true if User.current.logged?
70 81 require_login if Setting.login_required?
71 82 end
72 83
73 84 def set_localization
74 85 lang = nil
75 86 if User.current.logged?
76 87 lang = find_language(User.current.language)
77 88 end
78 89 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
79 90 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
80 91 if !accept_lang.blank?
81 92 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
82 93 end
83 94 end
84 95 lang ||= Setting.default_language
85 96 set_language_if_valid(lang)
86 97 end
87 98
88 99 def require_login
89 100 if !User.current.logged?
90 101 redirect_to :controller => "account", :action => "login", :back_url => url_for(params)
91 102 return false
92 103 end
93 104 true
94 105 end
95 106
96 107 def require_admin
97 108 return unless require_login
98 109 if !User.current.admin?
99 110 render_403
100 111 return false
101 112 end
102 113 true
103 114 end
104 115
105 116 def deny_access
106 117 User.current.logged? ? render_403 : require_login
107 118 end
108 119
109 120 # Authorize the user for the requested action
110 121 def authorize(ctrl = params[:controller], action = params[:action])
111 122 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project)
112 123 allowed ? true : deny_access
113 124 end
114 125
115 126 # make sure that the user is a member of the project (or admin) if project is private
116 127 # used as a before_filter for actions that do not require any particular permission on the project
117 128 def check_project_privacy
118 129 if @project && @project.active?
119 130 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
120 131 true
121 132 else
122 133 User.current.logged? ? render_403 : require_login
123 134 end
124 135 else
125 136 @project = nil
126 137 render_404
127 138 false
128 139 end
129 140 end
130 141
131 142 def redirect_back_or_default(default)
132 143 back_url = CGI.unescape(params[:back_url].to_s)
133 144 if !back_url.blank?
134 145 begin
135 146 uri = URI.parse(back_url)
136 147 # do not redirect user to another host or to the login or register page
137 148 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
138 149 redirect_to(back_url) and return
139 150 end
140 151 rescue URI::InvalidURIError
141 152 # redirect to default
142 153 end
143 154 end
144 155 redirect_to default
145 156 end
146 157
147 158 def render_403
148 159 @project = nil
149 160 render :template => "common/403", :layout => !request.xhr?, :status => 403
150 161 return false
151 162 end
152 163
153 164 def render_404
154 165 render :template => "common/404", :layout => !request.xhr?, :status => 404
155 166 return false
156 167 end
157 168
158 169 def render_error(msg)
159 170 flash.now[:error] = msg
160 171 render :text => '', :layout => !request.xhr?, :status => 500
161 172 end
162 173
163 174 def render_feed(items, options={})
164 175 @items = items || []
165 176 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
166 177 @items = @items.slice(0, Setting.feeds_limit.to_i)
167 178 @title = options[:title] || Setting.app_title
168 179 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
169 180 end
170 181
171 182 def self.accept_key_auth(*actions)
172 183 actions = actions.flatten.map(&:to_s)
173 184 write_inheritable_attribute('accept_key_auth_actions', actions)
174 185 end
175 186
176 187 def accept_key_auth_actions
177 188 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
178 189 end
179 190
180 191 # TODO: move to model
181 192 def attach_files(obj, attachments)
182 193 attached = []
183 194 unsaved = []
184 195 if attachments && attachments.is_a?(Hash)
185 196 attachments.each_value do |attachment|
186 197 file = attachment['file']
187 198 next unless file && file.size > 0
188 199 a = Attachment.create(:container => obj,
189 200 :file => file,
190 201 :description => attachment['description'].to_s.strip,
191 202 :author => User.current)
192 203 a.new_record? ? (unsaved << a) : (attached << a)
193 204 end
194 205 if unsaved.any?
195 206 flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
196 207 end
197 208 end
198 209 attached
199 210 end
200 211
201 212 # Returns the number of objects that should be displayed
202 213 # on the paginated list
203 214 def per_page_option
204 215 per_page = nil
205 216 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
206 217 per_page = params[:per_page].to_s.to_i
207 218 session[:per_page] = per_page
208 219 elsif session[:per_page]
209 220 per_page = session[:per_page]
210 221 else
211 222 per_page = Setting.per_page_options_array.first || 25
212 223 end
213 224 per_page
214 225 end
215 226
216 227 # qvalues http header parser
217 228 # code taken from webrick
218 229 def parse_qvalues(value)
219 230 tmp = []
220 231 if value
221 232 parts = value.split(/,\s*/)
222 233 parts.each {|part|
223 234 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
224 235 val = m[1]
225 236 q = (m[2] or 1).to_f
226 237 tmp.push([val, q])
227 238 end
228 239 }
229 240 tmp = tmp.sort_by{|val, q| -q}
230 241 tmp.collect!{|val, q| val}
231 242 end
232 243 return tmp
233 244 rescue
234 245 nil
235 246 end
236 247
237 248 # Returns a string that can be used as filename value in Content-Disposition header
238 249 def filename_for_content_disposition(name)
239 250 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
240 251 end
241 252 end
@@ -1,319 +1,323
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "digest/sha1"
19 19
20 20 class User < ActiveRecord::Base
21 21
22 22 # Account statuses
23 23 STATUS_ANONYMOUS = 0
24 24 STATUS_ACTIVE = 1
25 25 STATUS_REGISTERED = 2
26 26 STATUS_LOCKED = 3
27 27
28 28 USER_FORMATS = {
29 29 :firstname_lastname => '#{firstname} #{lastname}',
30 30 :firstname => '#{firstname}',
31 31 :lastname_firstname => '#{lastname} #{firstname}',
32 32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 33 :username => '#{login}'
34 34 }
35 35
36 36 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
37 37 has_many :members, :dependent => :delete_all
38 38 has_many :projects, :through => :memberships
39 39 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
40 40 has_many :changesets, :dependent => :nullify
41 41 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
42 42 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
43 43 belongs_to :auth_source
44 44
45 45 # Active non-anonymous users scope
46 46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
47 47
48 48 acts_as_customizable
49 49
50 50 attr_accessor :password, :password_confirmation
51 51 attr_accessor :last_before_login_on
52 52 # Prevents unauthorized assignments
53 53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
54 54
55 55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
56 56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
57 57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
58 58 # Login must contain lettres, numbers, underscores only
59 59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
60 60 validates_length_of :login, :maximum => 30
61 61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
62 62 validates_length_of :firstname, :lastname, :maximum => 30
63 63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
64 64 validates_length_of :mail, :maximum => 60, :allow_nil => true
65 65 validates_length_of :password, :minimum => 4, :allow_nil => true
66 66 validates_confirmation_of :password, :allow_nil => true
67 67
68 68 def before_create
69 69 self.mail_notification = false
70 70 true
71 71 end
72 72
73 73 def before_save
74 74 # update hashed_password if password was set
75 75 self.hashed_password = User.hash_password(self.password) if self.password
76 76 end
77 77
78 78 def reload(*args)
79 79 @name = nil
80 80 super
81 81 end
82 82
83 83 def identity_url=(url)
84 84 if url.blank?
85 85 write_attribute(:identity_url, '')
86 86 else
87 87 begin
88 88 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
89 89 rescue OpenIdAuthentication::InvalidOpenId
90 90 # Invlaid url, don't save
91 91 end
92 92 end
93 93 self.read_attribute(:identity_url)
94 94 end
95 95
96 96 # Returns the user that matches provided login and password, or nil
97 97 def self.try_to_login(login, password)
98 98 # Make sure no one can sign in with an empty password
99 99 return nil if password.to_s.empty?
100 100 user = find(:first, :conditions => ["login=?", login])
101 101 if user
102 102 # user is already in local database
103 103 return nil if !user.active?
104 104 if user.auth_source
105 105 # user has an external authentication method
106 106 return nil unless user.auth_source.authenticate(login, password)
107 107 else
108 108 # authentication with local password
109 109 return nil unless User.hash_password(password) == user.hashed_password
110 110 end
111 111 else
112 112 # user is not yet registered, try to authenticate with available sources
113 113 attrs = AuthSource.authenticate(login, password)
114 114 if attrs
115 115 user = new(*attrs)
116 116 user.login = login
117 117 user.language = Setting.default_language
118 118 if user.save
119 119 user.reload
120 120 logger.info("User '#{user.login}' created from the LDAP") if logger
121 121 end
122 122 end
123 123 end
124 124 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
125 125 user
126 126 rescue => text
127 127 raise text
128 128 end
129
130 # Returns the user who matches the given autologin +key+ or nil
131 def self.try_to_autologin(key)
132 token = Token.find_by_action_and_value('autologin', key)
133 if token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
134 token.user.update_attribute(:last_login_on, Time.now)
135 token.user
136 end
137 end
129 138
130 139 # Return user's full name for display
131 140 def name(formatter = nil)
132 141 if formatter
133 142 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
134 143 else
135 144 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
136 145 end
137 146 end
138 147
139 148 def active?
140 149 self.status == STATUS_ACTIVE
141 150 end
142 151
143 152 def registered?
144 153 self.status == STATUS_REGISTERED
145 154 end
146 155
147 156 def locked?
148 157 self.status == STATUS_LOCKED
149 158 end
150 159
151 160 def check_password?(clear_password)
152 161 User.hash_password(clear_password) == self.hashed_password
153 162 end
154 163
155 164 # Generate and set a random password. Useful for automated user creation
156 165 # Based on Token#generate_token_value
157 166 #
158 167 def random_password
159 168 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
160 169 password = ''
161 170 40.times { |i| password << chars[rand(chars.size-1)] }
162 171 self.password = password
163 172 self.password_confirmation = password
164 173 self
165 174 end
166 175
167 176 def pref
168 177 self.preference ||= UserPreference.new(:user => self)
169 178 end
170 179
171 180 def time_zone
172 181 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
173 182 end
174 183
175 184 def wants_comments_in_reverse_order?
176 185 self.pref[:comments_sorting] == 'desc'
177 186 end
178 187
179 188 # Return user's RSS key (a 40 chars long string), used to access feeds
180 189 def rss_key
181 190 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
182 191 token.value
183 192 end
184 193
185 194 # Return an array of project ids for which the user has explicitly turned mail notifications on
186 195 def notified_projects_ids
187 196 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
188 197 end
189 198
190 199 def notified_project_ids=(ids)
191 200 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
192 201 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
193 202 @notified_projects_ids = nil
194 203 notified_projects_ids
195 204 end
196 205
197 206 def self.find_by_rss_key(key)
198 207 token = Token.find_by_value(key)
199 208 token && token.user.active? ? token.user : nil
200 209 end
201 210
202 def self.find_by_autologin_key(key)
203 token = Token.find_by_action_and_value('autologin', key)
204 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
205 end
206
207 211 # Makes find_by_mail case-insensitive
208 212 def self.find_by_mail(mail)
209 213 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
210 214 end
211 215
212 216 # Sort users by their display names
213 217 def <=>(user)
214 218 self.to_s.downcase <=> user.to_s.downcase
215 219 end
216 220
217 221 def to_s
218 222 name
219 223 end
220 224
221 225 def logged?
222 226 true
223 227 end
224 228
225 229 def anonymous?
226 230 !logged?
227 231 end
228 232
229 233 # Return user's role for project
230 234 def role_for_project(project)
231 235 # No role on archived projects
232 236 return nil unless project && project.active?
233 237 if logged?
234 238 # Find project membership
235 239 membership = memberships.detect {|m| m.project_id == project.id}
236 240 if membership
237 241 membership.role
238 242 else
239 243 @role_non_member ||= Role.non_member
240 244 end
241 245 else
242 246 @role_anonymous ||= Role.anonymous
243 247 end
244 248 end
245 249
246 250 # Return true if the user is a member of project
247 251 def member_of?(project)
248 252 role_for_project(project).member?
249 253 end
250 254
251 255 # Return true if the user is allowed to do the specified action on project
252 256 # action can be:
253 257 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
254 258 # * a permission Symbol (eg. :edit_project)
255 259 def allowed_to?(action, project, options={})
256 260 if project
257 261 # No action allowed on archived projects
258 262 return false unless project.active?
259 263 # No action allowed on disabled modules
260 264 return false unless project.allows_to?(action)
261 265 # Admin users are authorized for anything else
262 266 return true if admin?
263 267
264 268 role = role_for_project(project)
265 269 return false unless role
266 270 role.allowed_to?(action) && (project.is_public? || role.member?)
267 271
268 272 elsif options[:global]
269 273 # authorize if user has at least one role that has this permission
270 274 roles = memberships.collect {|m| m.role}.uniq
271 275 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
272 276 else
273 277 false
274 278 end
275 279 end
276 280
277 281 def self.current=(user)
278 282 @current_user = user
279 283 end
280 284
281 285 def self.current
282 286 @current_user ||= User.anonymous
283 287 end
284 288
285 289 def self.anonymous
286 290 anonymous_user = AnonymousUser.find(:first)
287 291 if anonymous_user.nil?
288 292 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
289 293 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
290 294 end
291 295 anonymous_user
292 296 end
293 297
294 298 private
295 299 # Return password digest
296 300 def self.hash_password(clear_password)
297 301 Digest::SHA1.hexdigest(clear_password || "")
298 302 end
299 303 end
300 304
301 305 class AnonymousUser < User
302 306
303 307 def validate_on_create
304 308 # There should be only one AnonymousUser in the database
305 309 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
306 310 end
307 311
308 312 def available_custom_fields
309 313 []
310 314 end
311 315
312 316 # Overrides a few properties
313 317 def logged?; false end
314 318 def admin; false end
315 319 def name; 'Anonymous' end
316 320 def mail; nil end
317 321 def time_zone; nil end
318 322 def rss_key; nil end
319 323 end
@@ -1,181 +1,169
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'account_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class AccountController; def rescue_action(e) raise e end; end
23 23
24 24 class AccountControllerTest < Test::Unit::TestCase
25 25 fixtures :users, :roles
26 26
27 27 def setup
28 28 @controller = AccountController.new
29 29 @request = ActionController::TestRequest.new
30 30 @response = ActionController::TestResponse.new
31 31 User.current = nil
32 32 end
33 33
34 34 def test_show
35 35 get :show, :id => 2
36 36 assert_response :success
37 37 assert_template 'show'
38 38 assert_not_nil assigns(:user)
39 39 end
40 40
41 41 def test_show_inactive
42 42 get :show, :id => 5
43 43 assert_response 404
44 44 assert_nil assigns(:user)
45 45 end
46 46
47 47 def test_login_should_redirect_to_back_url_param
48 48 # request.uri is "test.host" in test environment
49 49 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.host%2Fissues%2Fshow%2F1'
50 50 assert_redirected_to '/issues/show/1'
51 51 end
52 52
53 53 def test_login_should_not_redirect_to_another_host
54 54 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.foo%2Ffake'
55 55 assert_redirected_to '/my/page'
56 56 end
57 57
58 58 def test_login_with_wrong_password
59 59 post :login, :username => 'admin', :password => 'bad'
60 60 assert_response :success
61 61 assert_template 'login'
62 62 assert_tag 'div',
63 63 :attributes => { :class => "flash error" },
64 64 :content => /Invalid user or password/
65 65 end
66 66
67 67 if Object.const_defined?(:OpenID)
68 68
69 69 def test_login_with_openid_for_existing_user
70 70 Setting.self_registration = '3'
71 71 Setting.openid = '1'
72 72 existing_user = User.new(:firstname => 'Cool',
73 73 :lastname => 'User',
74 74 :mail => 'user@somedomain.com',
75 75 :identity_url => 'http://openid.example.com/good_user')
76 76 existing_user.login = 'cool_user'
77 77 assert existing_user.save!
78 78
79 79 post :login, :openid_url => existing_user.identity_url
80 80 assert_redirected_to 'my/page'
81 81 end
82 82
83 83 def test_login_with_openid_for_existing_non_active_user
84 84 Setting.self_registration = '2'
85 85 Setting.openid = '1'
86 86 existing_user = User.new(:firstname => 'Cool',
87 87 :lastname => 'User',
88 88 :mail => 'user@somedomain.com',
89 89 :identity_url => 'http://openid.example.com/good_user',
90 90 :status => User::STATUS_REGISTERED)
91 91 existing_user.login = 'cool_user'
92 92 assert existing_user.save!
93 93
94 94 post :login, :openid_url => existing_user.identity_url
95 95 assert_redirected_to 'login'
96 96 end
97 97
98 98 def test_login_with_openid_with_new_user_created
99 99 Setting.self_registration = '3'
100 100 Setting.openid = '1'
101 101 post :login, :openid_url => 'http://openid.example.com/good_user'
102 102 assert_redirected_to 'my/account'
103 103 user = User.find_by_login('cool_user')
104 104 assert user
105 105 assert_equal 'Cool', user.firstname
106 106 assert_equal 'User', user.lastname
107 107 end
108 108
109 109 def test_login_with_openid_with_new_user_and_self_registration_off
110 110 Setting.self_registration = '0'
111 111 Setting.openid = '1'
112 112 post :login, :openid_url => 'http://openid.example.com/good_user'
113 113 assert_redirected_to home_url
114 114 user = User.find_by_login('cool_user')
115 115 assert ! user
116 116 end
117 117
118 118 def test_login_with_openid_with_new_user_created_with_email_activation_should_have_a_token
119 119 Setting.self_registration = '1'
120 120 Setting.openid = '1'
121 121 post :login, :openid_url => 'http://openid.example.com/good_user'
122 122 assert_redirected_to 'login'
123 123 user = User.find_by_login('cool_user')
124 124 assert user
125 125
126 126 token = Token.find_by_user_id_and_action(user.id, 'register')
127 127 assert token
128 128 end
129 129
130 130 def test_login_with_openid_with_new_user_created_with_manual_activation
131 131 Setting.self_registration = '2'
132 132 Setting.openid = '1'
133 133 post :login, :openid_url => 'http://openid.example.com/good_user'
134 134 assert_redirected_to 'login'
135 135 user = User.find_by_login('cool_user')
136 136 assert user
137 137 assert_equal User::STATUS_REGISTERED, user.status
138 138 end
139 139
140 140 def test_login_with_openid_with_new_user_with_conflict_should_register
141 141 Setting.self_registration = '3'
142 142 Setting.openid = '1'
143 143 existing_user = User.new(:firstname => 'Cool', :lastname => 'User', :mail => 'user@somedomain.com')
144 144 existing_user.login = 'cool_user'
145 145 assert existing_user.save!
146 146
147 147 post :login, :openid_url => 'http://openid.example.com/good_user'
148 148 assert_response :success
149 149 assert_template 'register'
150 150 assert assigns(:user)
151 151 assert_equal 'http://openid.example.com/good_user', assigns(:user)[:identity_url]
152 152 end
153 153
154 154 def test_setting_openid_should_return_true_when_set_to_true
155 155 Setting.openid = '1'
156 156 assert_equal true, Setting.openid?
157 157 end
158 158
159 159 else
160 160 puts "Skipping openid tests."
161 161 end
162 162
163
164 def test_autologin
165 Setting.autologin = "7"
166 Token.delete_all
167 post :login, :username => 'admin', :password => 'admin', :autologin => 1
168 assert_redirected_to 'my/page'
169 token = Token.find :first
170 assert_not_nil token
171 assert_equal User.find_by_login('admin'), token.user
172 assert_equal 'autologin', token.action
173 end
174
175 163 def test_logout
176 164 @request.session[:user_id] = 2
177 165 get :logout
178 166 assert_redirected_to ''
179 167 assert_nil @request.session[:user_id]
180 168 end
181 169 end
@@ -1,153 +1,185
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "#{File.dirname(__FILE__)}/../test_helper"
19 19
20 20 begin
21 21 require 'mocha'
22 22 rescue
23 23 # Won't run some tests
24 24 end
25 25
26 26 class AccountTest < ActionController::IntegrationTest
27 27 fixtures :users
28 28
29 29 # Replace this with your real tests.
30 30 def test_login
31 31 get "my/page"
32 32 assert_redirected_to "/login?back_url=http%3A%2F%2Fwww.example.com%2Fmy%2Fpage"
33 33 log_user('jsmith', 'jsmith')
34 34
35 35 get "my/account"
36 36 assert_response :success
37 37 assert_template "my/account"
38 38 end
39 39
40 def test_autologin
41 user = User.find(1)
42 Setting.autologin = "7"
43 Token.delete_all
44
45 # User logs in with 'autologin' checked
46 post '/login', :username => user.login, :password => 'admin', :autologin => 1
47 assert_redirected_to 'my/page'
48 token = Token.find :first
49 assert_not_nil token
50 assert_equal user, token.user
51 assert_equal 'autologin', token.action
52 assert_equal user.id, session[:user_id]
53 assert_equal token.value, cookies['autologin']
54
55 # Session is cleared
56 reset!
57 User.current = nil
58 # Clears user's last login timestamp
59 user.update_attribute :last_login_on, nil
60 assert_nil user.reload.last_login_on
61
62 # User comes back with his autologin cookie
63 cookies[:autologin] = token.value
64 get '/my/page'
65 assert_response :success
66 assert_template 'my/page'
67 assert_equal user.id, session[:user_id]
68 assert_not_nil user.reload.last_login_on
69 assert user.last_login_on > 2.second.ago
70 end
71
40 72 def test_lost_password
41 73 Token.delete_all
42 74
43 75 get "account/lost_password"
44 76 assert_response :success
45 77 assert_template "account/lost_password"
46 78
47 79 post "account/lost_password", :mail => 'jSmith@somenet.foo'
48 80 assert_redirected_to "/login"
49 81
50 82 token = Token.find(:first)
51 83 assert_equal 'recovery', token.action
52 84 assert_equal 'jsmith@somenet.foo', token.user.mail
53 85 assert !token.expired?
54 86
55 87 get "account/lost_password", :token => token.value
56 88 assert_response :success
57 89 assert_template "account/password_recovery"
58 90
59 91 post "account/lost_password", :token => token.value, :new_password => 'newpass', :new_password_confirmation => 'newpass'
60 92 assert_redirected_to "/login"
61 93 assert_equal 'Password was successfully updated.', flash[:notice]
62 94
63 95 log_user('jsmith', 'newpass')
64 96 assert_equal 0, Token.count
65 97 end
66 98
67 99 def test_register_with_automatic_activation
68 100 Setting.self_registration = '3'
69 101
70 102 get 'account/register'
71 103 assert_response :success
72 104 assert_template 'account/register'
73 105
74 106 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
75 107 :password => "newpass", :password_confirmation => "newpass"
76 108 assert_redirected_to 'my/account'
77 109 follow_redirect!
78 110 assert_response :success
79 111 assert_template 'my/account'
80 112
81 113 assert User.find_by_login('newuser').active?
82 114 end
83 115
84 116 def test_register_with_manual_activation
85 117 Setting.self_registration = '2'
86 118
87 119 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
88 120 :password => "newpass", :password_confirmation => "newpass"
89 121 assert_redirected_to '/login'
90 122 assert !User.find_by_login('newuser').active?
91 123 end
92 124
93 125 def test_register_with_email_activation
94 126 Setting.self_registration = '1'
95 127 Token.delete_all
96 128
97 129 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
98 130 :password => "newpass", :password_confirmation => "newpass"
99 131 assert_redirected_to '/login'
100 132 assert !User.find_by_login('newuser').active?
101 133
102 134 token = Token.find(:first)
103 135 assert_equal 'register', token.action
104 136 assert_equal 'newuser@foo.bar', token.user.mail
105 137 assert !token.expired?
106 138
107 139 get 'account/activate', :token => token.value
108 140 assert_redirected_to '/login'
109 141 log_user('newuser', 'newpass')
110 142 end
111 143
112 144 if Object.const_defined?(:Mocha)
113 145
114 146 def test_onthefly_registration
115 147 # disable registration
116 148 Setting.self_registration = '0'
117 149 AuthSource.expects(:authenticate).returns([:login => 'foo', :firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com', :auth_source_id => 66])
118 150
119 151 post 'account/login', :username => 'foo', :password => 'bar'
120 152 assert_redirected_to 'my/page'
121 153
122 154 user = User.find_by_login('foo')
123 155 assert user.is_a?(User)
124 156 assert_equal 66, user.auth_source_id
125 157 assert user.hashed_password.blank?
126 158 end
127 159
128 160 def test_onthefly_registration_with_invalid_attributes
129 161 # disable registration
130 162 Setting.self_registration = '0'
131 163 AuthSource.expects(:authenticate).returns([:login => 'foo', :lastname => 'Smith', :auth_source_id => 66])
132 164
133 165 post 'account/login', :username => 'foo', :password => 'bar'
134 166 assert_response :success
135 167 assert_template 'account/register'
136 168 assert_tag :input, :attributes => { :name => 'user[firstname]', :value => '' }
137 169 assert_tag :input, :attributes => { :name => 'user[lastname]', :value => 'Smith' }
138 170 assert_no_tag :input, :attributes => { :name => 'user[login]' }
139 171 assert_no_tag :input, :attributes => { :name => 'user[password]' }
140 172
141 173 post 'account/register', :user => {:firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com'}
142 174 assert_redirected_to '/my/account'
143 175
144 176 user = User.find_by_login('foo')
145 177 assert user.is_a?(User)
146 178 assert_equal 66, user.auth_source_id
147 179 assert user.hashed_password.blank?
148 180 end
149 181
150 182 else
151 183 puts 'Mocha is missing. Skipping tests.'
152 184 end
153 185 end
General Comments 0
You need to be logged in to leave comments. Login now