##// END OF EJS Templates
Reset session on login/logout (#4248)....
Jean-Philippe Lang -
r2966:0485d3a52408
parent child
Show More
@@ -1,259 +1,259
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 layout 'base'
25 25
26 26 before_filter :user_setup, :check_if_login_required, :set_localization
27 27 filter_parameter_logging :password
28 28 protect_from_forgery
29 29
30 30 include Redmine::Search::Controller
31 31 include Redmine::MenuManager::MenuController
32 32 helper Redmine::MenuManager::MenuHelper
33 33
34 34 REDMINE_SUPPORTED_SCM.each do |scm|
35 35 require_dependency "repository/#{scm.underscore}"
36 36 end
37 37
38 38 def user_setup
39 39 # Check the settings cache for each request
40 40 Setting.check_cache
41 41 # Find the current user
42 42 User.current = find_current_user
43 43 end
44 44
45 45 # Returns the current user or nil if no user is logged in
46 46 # and starts a session if needed
47 47 def find_current_user
48 48 if session[:user_id]
49 49 # existing session
50 50 (User.active.find(session[:user_id]) rescue nil)
51 51 elsif cookies[:autologin] && Setting.autologin?
52 52 # auto-login feature starts a new session
53 53 user = User.try_to_autologin(cookies[:autologin])
54 54 session[:user_id] = user.id if user
55 55 user
56 56 elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
57 57 # RSS key authentication does not start a session
58 58 User.find_by_rss_key(params[:key])
59 59 end
60 60 end
61 61
62 62 # Sets the logged in user
63 63 def logged_user=(user)
64 reset_session
64 65 if user && user.is_a?(User)
65 66 User.current = user
66 67 session[:user_id] = user.id
67 68 else
68 69 User.current = User.anonymous
69 session[:user_id] = nil
70 70 end
71 71 end
72 72
73 73 # check if login is globally required to access the application
74 74 def check_if_login_required
75 75 # no check needed if user is already logged in
76 76 return true if User.current.logged?
77 77 require_login if Setting.login_required?
78 78 end
79 79
80 80 def set_localization
81 81 lang = nil
82 82 if User.current.logged?
83 83 lang = find_language(User.current.language)
84 84 end
85 85 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
86 86 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
87 87 if !accept_lang.blank?
88 88 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
89 89 end
90 90 end
91 91 lang ||= Setting.default_language
92 92 set_language_if_valid(lang)
93 93 end
94 94
95 95 def require_login
96 96 if !User.current.logged?
97 97 # Extract only the basic url parameters on non-GET requests
98 98 if request.get?
99 99 url = url_for(params)
100 100 else
101 101 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
102 102 end
103 103 redirect_to :controller => "account", :action => "login", :back_url => url
104 104 return false
105 105 end
106 106 true
107 107 end
108 108
109 109 def require_admin
110 110 return unless require_login
111 111 if !User.current.admin?
112 112 render_403
113 113 return false
114 114 end
115 115 true
116 116 end
117 117
118 118 def deny_access
119 119 User.current.logged? ? render_403 : require_login
120 120 end
121 121
122 122 # Authorize the user for the requested action
123 123 def authorize(ctrl = params[:controller], action = params[:action], global = false)
124 124 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
125 125 allowed ? true : deny_access
126 126 end
127 127
128 128 # Authorize the user for the requested action outside a project
129 129 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
130 130 authorize(ctrl, action, global)
131 131 end
132 132
133 133 # make sure that the user is a member of the project (or admin) if project is private
134 134 # used as a before_filter for actions that do not require any particular permission on the project
135 135 def check_project_privacy
136 136 if @project && @project.active?
137 137 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
138 138 true
139 139 else
140 140 User.current.logged? ? render_403 : require_login
141 141 end
142 142 else
143 143 @project = nil
144 144 render_404
145 145 false
146 146 end
147 147 end
148 148
149 149 def redirect_back_or_default(default)
150 150 back_url = CGI.unescape(params[:back_url].to_s)
151 151 if !back_url.blank?
152 152 begin
153 153 uri = URI.parse(back_url)
154 154 # do not redirect user to another host or to the login or register page
155 155 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
156 156 redirect_to(back_url) and return
157 157 end
158 158 rescue URI::InvalidURIError
159 159 # redirect to default
160 160 end
161 161 end
162 162 redirect_to default
163 163 end
164 164
165 165 def render_403
166 166 @project = nil
167 167 render :template => "common/403", :layout => !request.xhr?, :status => 403
168 168 return false
169 169 end
170 170
171 171 def render_404
172 172 render :template => "common/404", :layout => !request.xhr?, :status => 404
173 173 return false
174 174 end
175 175
176 176 def render_error(msg)
177 177 flash.now[:error] = msg
178 178 render :text => '', :layout => !request.xhr?, :status => 500
179 179 end
180 180
181 181 def render_feed(items, options={})
182 182 @items = items || []
183 183 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
184 184 @items = @items.slice(0, Setting.feeds_limit.to_i)
185 185 @title = options[:title] || Setting.app_title
186 186 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
187 187 end
188 188
189 189 def self.accept_key_auth(*actions)
190 190 actions = actions.flatten.map(&:to_s)
191 191 write_inheritable_attribute('accept_key_auth_actions', actions)
192 192 end
193 193
194 194 def accept_key_auth_actions
195 195 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
196 196 end
197 197
198 198 # TODO: move to model
199 199 def attach_files(obj, attachments)
200 200 attached = []
201 201 unsaved = []
202 202 if attachments && attachments.is_a?(Hash)
203 203 attachments.each_value do |attachment|
204 204 file = attachment['file']
205 205 next unless file && file.size > 0
206 206 a = Attachment.create(:container => obj,
207 207 :file => file,
208 208 :description => attachment['description'].to_s.strip,
209 209 :author => User.current)
210 210 a.new_record? ? (unsaved << a) : (attached << a)
211 211 end
212 212 if unsaved.any?
213 213 flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
214 214 end
215 215 end
216 216 attached
217 217 end
218 218
219 219 # Returns the number of objects that should be displayed
220 220 # on the paginated list
221 221 def per_page_option
222 222 per_page = nil
223 223 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
224 224 per_page = params[:per_page].to_s.to_i
225 225 session[:per_page] = per_page
226 226 elsif session[:per_page]
227 227 per_page = session[:per_page]
228 228 else
229 229 per_page = Setting.per_page_options_array.first || 25
230 230 end
231 231 per_page
232 232 end
233 233
234 234 # qvalues http header parser
235 235 # code taken from webrick
236 236 def parse_qvalues(value)
237 237 tmp = []
238 238 if value
239 239 parts = value.split(/,\s*/)
240 240 parts.each {|part|
241 241 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
242 242 val = m[1]
243 243 q = (m[2] or 1).to_f
244 244 tmp.push([val, q])
245 245 end
246 246 }
247 247 tmp = tmp.sort_by{|val, q| -q}
248 248 tmp.collect!{|val, q| val}
249 249 end
250 250 return tmp
251 251 rescue
252 252 nil
253 253 end
254 254
255 255 # Returns a string that can be used as filename value in Content-Disposition header
256 256 def filename_for_content_disposition(name)
257 257 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
258 258 end
259 259 end
@@ -1,188 +1,206
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 40 def test_autologin
41 41 user = User.find(1)
42 42 Setting.autologin = "7"
43 43 Token.delete_all
44 44
45 45 # User logs in with 'autologin' checked
46 46 post '/login', :username => user.login, :password => 'admin', :autologin => 1
47 47 assert_redirected_to 'my/page'
48 48 token = Token.find :first
49 49 assert_not_nil token
50 50 assert_equal user, token.user
51 51 assert_equal 'autologin', token.action
52 52 assert_equal user.id, session[:user_id]
53 53 assert_equal token.value, cookies['autologin']
54 54
55 55 # Session is cleared
56 56 reset!
57 57 User.current = nil
58 58 # Clears user's last login timestamp
59 59 user.update_attribute :last_login_on, nil
60 60 assert_nil user.reload.last_login_on
61 61
62 62 # User comes back with his autologin cookie
63 63 cookies[:autologin] = token.value
64 64 get '/my/page'
65 65 assert_response :success
66 66 assert_template 'my/page'
67 67 assert_equal user.id, session[:user_id]
68 68 assert_not_nil user.reload.last_login_on
69 69 assert user.last_login_on.utc > 10.second.ago.utc
70 70 end
71 71
72 72 def test_lost_password
73 73 Token.delete_all
74 74
75 75 get "account/lost_password"
76 76 assert_response :success
77 77 assert_template "account/lost_password"
78 78
79 79 post "account/lost_password", :mail => 'jSmith@somenet.foo'
80 80 assert_redirected_to "/login"
81 81
82 82 token = Token.find(:first)
83 83 assert_equal 'recovery', token.action
84 84 assert_equal 'jsmith@somenet.foo', token.user.mail
85 85 assert !token.expired?
86 86
87 87 get "account/lost_password", :token => token.value
88 88 assert_response :success
89 89 assert_template "account/password_recovery"
90 90
91 91 post "account/lost_password", :token => token.value, :new_password => 'newpass', :new_password_confirmation => 'newpass'
92 92 assert_redirected_to "/login"
93 93 assert_equal 'Password was successfully updated.', flash[:notice]
94 94
95 95 log_user('jsmith', 'newpass')
96 96 assert_equal 0, Token.count
97 97 end
98 98
99 99 def test_register_with_automatic_activation
100 100 Setting.self_registration = '3'
101 101
102 102 get 'account/register'
103 103 assert_response :success
104 104 assert_template 'account/register'
105 105
106 106 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
107 107 :password => "newpass", :password_confirmation => "newpass"
108 108 assert_redirected_to 'my/account'
109 109 follow_redirect!
110 110 assert_response :success
111 111 assert_template 'my/account'
112 112
113 113 user = User.find_by_login('newuser')
114 114 assert_not_nil user
115 115 assert user.active?
116 116 assert_not_nil user.last_login_on
117 117 end
118 118
119 119 def test_register_with_manual_activation
120 120 Setting.self_registration = '2'
121 121
122 122 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
123 123 :password => "newpass", :password_confirmation => "newpass"
124 124 assert_redirected_to '/login'
125 125 assert !User.find_by_login('newuser').active?
126 126 end
127 127
128 128 def test_register_with_email_activation
129 129 Setting.self_registration = '1'
130 130 Token.delete_all
131 131
132 132 post 'account/register', :user => {:login => "newuser", :language => "en", :firstname => "New", :lastname => "User", :mail => "newuser@foo.bar"},
133 133 :password => "newpass", :password_confirmation => "newpass"
134 134 assert_redirected_to '/login'
135 135 assert !User.find_by_login('newuser').active?
136 136
137 137 token = Token.find(:first)
138 138 assert_equal 'register', token.action
139 139 assert_equal 'newuser@foo.bar', token.user.mail
140 140 assert !token.expired?
141 141
142 142 get 'account/activate', :token => token.value
143 143 assert_redirected_to '/login'
144 144 log_user('newuser', 'newpass')
145 145 end
146 146
147 147 if Object.const_defined?(:Mocha)
148 148
149 149 def test_onthefly_registration
150 150 # disable registration
151 151 Setting.self_registration = '0'
152 152 AuthSource.expects(:authenticate).returns([:login => 'foo', :firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com', :auth_source_id => 66])
153 153
154 154 post 'account/login', :username => 'foo', :password => 'bar'
155 155 assert_redirected_to 'my/page'
156 156
157 157 user = User.find_by_login('foo')
158 158 assert user.is_a?(User)
159 159 assert_equal 66, user.auth_source_id
160 160 assert user.hashed_password.blank?
161 161 end
162 162
163 163 def test_onthefly_registration_with_invalid_attributes
164 164 # disable registration
165 165 Setting.self_registration = '0'
166 166 AuthSource.expects(:authenticate).returns([:login => 'foo', :lastname => 'Smith', :auth_source_id => 66])
167 167
168 168 post 'account/login', :username => 'foo', :password => 'bar'
169 169 assert_response :success
170 170 assert_template 'account/register'
171 171 assert_tag :input, :attributes => { :name => 'user[firstname]', :value => '' }
172 172 assert_tag :input, :attributes => { :name => 'user[lastname]', :value => 'Smith' }
173 173 assert_no_tag :input, :attributes => { :name => 'user[login]' }
174 174 assert_no_tag :input, :attributes => { :name => 'user[password]' }
175 175
176 176 post 'account/register', :user => {:firstname => 'Foo', :lastname => 'Smith', :mail => 'foo@bar.com'}
177 177 assert_redirected_to '/my/account'
178 178
179 179 user = User.find_by_login('foo')
180 180 assert user.is_a?(User)
181 181 assert_equal 66, user.auth_source_id
182 182 assert user.hashed_password.blank?
183 183 end
184 184
185 def test_login_and_logout_should_clear_session
186 get '/login'
187 sid = session[:session_id]
188
189 post '/login', :username => 'admin', :password => 'admin'
190 assert_redirected_to 'my/page'
191 assert_not_equal sid, session[:session_id], "login should reset session"
192 assert_equal 1, session[:user_id]
193 sid = session[:session_id]
194
195 get '/'
196 assert_equal sid, session[:session_id]
197
198 get '/logout'
199 assert_not_equal sid, session[:session_id], "logout should reset session"
200 assert_nil session[:user_id]
201 end
202
185 203 else
186 204 puts 'Mocha is missing. Skipping tests.'
187 205 end
188 206 end
General Comments 0
You need to be logged in to leave comments. Login now