##// END OF EJS Templates
Merged r12311 (#15427)....
Jean-Philippe Lang -
r12081:a74ffeff0749
parent child
Show More
@@ -0,0 +1,41
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../../test_helper', __FILE__)
19
20 class Redmine::ApiTest::ApiTest < Redmine::ApiTest::Base
21 fixtures :users
22
23 def setup
24 Setting.rest_api_enabled = '1'
25 end
26
27 def test_api_should_work_with_protect_from_forgery
28 ActionController::Base.allow_forgery_protection = true
29 assert_difference('User.count') do
30 post '/users.xml', {
31 :user => {
32 :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
33 :mail => 'foo@example.net', :password => 'secret123'}
34 },
35 credentials('admin')
36 assert_response 201
37 end
38 ensure
39 ActionController::Base.allow_forgery_protection = false
40 end
41 end No newline at end of file
@@ -1,611 +1,617
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 'uri'
18 require 'uri'
19 require 'cgi'
19 require 'cgi'
20
20
21 class Unauthorized < Exception; end
21 class Unauthorized < Exception; end
22
22
23 class ApplicationController < ActionController::Base
23 class ApplicationController < ActionController::Base
24 include Redmine::I18n
24 include Redmine::I18n
25 include Redmine::Pagination
25 include Redmine::Pagination
26 include RoutesHelper
26 include RoutesHelper
27 helper :routes
27 helper :routes
28
28
29 class_attribute :accept_api_auth_actions
29 class_attribute :accept_api_auth_actions
30 class_attribute :accept_rss_auth_actions
30 class_attribute :accept_rss_auth_actions
31 class_attribute :model_object
31 class_attribute :model_object
32
32
33 layout 'base'
33 layout 'base'
34
34
35 protect_from_forgery
35 protect_from_forgery
36
37 def verify_authenticity_token
38 unless api_request?
39 super
40 end
41 end
42
36 def handle_unverified_request
43 def handle_unverified_request
37 super
44 unless api_request?
38 cookies.delete(autologin_cookie_name)
45 super
39 if api_request?
46 cookies.delete(autologin_cookie_name)
40 logger.error "API calls must include a proper Content-type header (application/xml or application/json)."
47 render_error :status => 422, :message => "Invalid form authenticity token."
41 end
48 end
42 render_error :status => 422, :message => "Invalid form authenticity token."
43 end
49 end
44
50
45 before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization
51 before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization
46
52
47 rescue_from ::Unauthorized, :with => :deny_access
53 rescue_from ::Unauthorized, :with => :deny_access
48 rescue_from ::ActionView::MissingTemplate, :with => :missing_template
54 rescue_from ::ActionView::MissingTemplate, :with => :missing_template
49
55
50 include Redmine::Search::Controller
56 include Redmine::Search::Controller
51 include Redmine::MenuManager::MenuController
57 include Redmine::MenuManager::MenuController
52 helper Redmine::MenuManager::MenuHelper
58 helper Redmine::MenuManager::MenuHelper
53
59
54 def session_expiration
60 def session_expiration
55 if session[:user_id]
61 if session[:user_id]
56 if session_expired? && !try_to_autologin
62 if session_expired? && !try_to_autologin
57 reset_session
63 reset_session
58 flash[:error] = l(:error_session_expired)
64 flash[:error] = l(:error_session_expired)
59 redirect_to signin_url
65 redirect_to signin_url
60 else
66 else
61 session[:atime] = Time.now.utc.to_i
67 session[:atime] = Time.now.utc.to_i
62 end
68 end
63 end
69 end
64 end
70 end
65
71
66 def session_expired?
72 def session_expired?
67 if Setting.session_lifetime?
73 if Setting.session_lifetime?
68 unless session[:ctime] && (Time.now.utc.to_i - session[:ctime].to_i <= Setting.session_lifetime.to_i * 60)
74 unless session[:ctime] && (Time.now.utc.to_i - session[:ctime].to_i <= Setting.session_lifetime.to_i * 60)
69 return true
75 return true
70 end
76 end
71 end
77 end
72 if Setting.session_timeout?
78 if Setting.session_timeout?
73 unless session[:atime] && (Time.now.utc.to_i - session[:atime].to_i <= Setting.session_timeout.to_i * 60)
79 unless session[:atime] && (Time.now.utc.to_i - session[:atime].to_i <= Setting.session_timeout.to_i * 60)
74 return true
80 return true
75 end
81 end
76 end
82 end
77 false
83 false
78 end
84 end
79
85
80 def start_user_session(user)
86 def start_user_session(user)
81 session[:user_id] = user.id
87 session[:user_id] = user.id
82 session[:ctime] = Time.now.utc.to_i
88 session[:ctime] = Time.now.utc.to_i
83 session[:atime] = Time.now.utc.to_i
89 session[:atime] = Time.now.utc.to_i
84 if user.must_change_password?
90 if user.must_change_password?
85 session[:pwd] = '1'
91 session[:pwd] = '1'
86 end
92 end
87 end
93 end
88
94
89 def user_setup
95 def user_setup
90 # Check the settings cache for each request
96 # Check the settings cache for each request
91 Setting.check_cache
97 Setting.check_cache
92 # Find the current user
98 # Find the current user
93 User.current = find_current_user
99 User.current = find_current_user
94 logger.info(" Current user: " + (User.current.logged? ? "#{User.current.login} (id=#{User.current.id})" : "anonymous")) if logger
100 logger.info(" Current user: " + (User.current.logged? ? "#{User.current.login} (id=#{User.current.id})" : "anonymous")) if logger
95 end
101 end
96
102
97 # Returns the current user or nil if no user is logged in
103 # Returns the current user or nil if no user is logged in
98 # and starts a session if needed
104 # and starts a session if needed
99 def find_current_user
105 def find_current_user
100 user = nil
106 user = nil
101 unless api_request?
107 unless api_request?
102 if session[:user_id]
108 if session[:user_id]
103 # existing session
109 # existing session
104 user = (User.active.find(session[:user_id]) rescue nil)
110 user = (User.active.find(session[:user_id]) rescue nil)
105 elsif autologin_user = try_to_autologin
111 elsif autologin_user = try_to_autologin
106 user = autologin_user
112 user = autologin_user
107 elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
113 elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth?
108 # RSS key authentication does not start a session
114 # RSS key authentication does not start a session
109 user = User.find_by_rss_key(params[:key])
115 user = User.find_by_rss_key(params[:key])
110 end
116 end
111 end
117 end
112 if user.nil? && Setting.rest_api_enabled? && accept_api_auth?
118 if user.nil? && Setting.rest_api_enabled? && accept_api_auth?
113 if (key = api_key_from_request)
119 if (key = api_key_from_request)
114 # Use API key
120 # Use API key
115 user = User.find_by_api_key(key)
121 user = User.find_by_api_key(key)
116 else
122 else
117 # HTTP Basic, either username/password or API key/random
123 # HTTP Basic, either username/password or API key/random
118 authenticate_with_http_basic do |username, password|
124 authenticate_with_http_basic do |username, password|
119 user = User.try_to_login(username, password) || User.find_by_api_key(username)
125 user = User.try_to_login(username, password) || User.find_by_api_key(username)
120 end
126 end
121 if user && user.must_change_password?
127 if user && user.must_change_password?
122 render_error :message => 'You must change your password', :status => 403
128 render_error :message => 'You must change your password', :status => 403
123 return
129 return
124 end
130 end
125 end
131 end
126 # Switch user if requested by an admin user
132 # Switch user if requested by an admin user
127 if user && user.admin? && (username = api_switch_user_from_request)
133 if user && user.admin? && (username = api_switch_user_from_request)
128 su = User.find_by_login(username)
134 su = User.find_by_login(username)
129 if su && su.active?
135 if su && su.active?
130 logger.info(" User switched by: #{user.login} (id=#{user.id})") if logger
136 logger.info(" User switched by: #{user.login} (id=#{user.id})") if logger
131 user = su
137 user = su
132 else
138 else
133 render_error :message => 'Invalid X-Redmine-Switch-User header', :status => 412
139 render_error :message => 'Invalid X-Redmine-Switch-User header', :status => 412
134 end
140 end
135 end
141 end
136 end
142 end
137 user
143 user
138 end
144 end
139
145
140 def autologin_cookie_name
146 def autologin_cookie_name
141 Redmine::Configuration['autologin_cookie_name'].presence || 'autologin'
147 Redmine::Configuration['autologin_cookie_name'].presence || 'autologin'
142 end
148 end
143
149
144 def try_to_autologin
150 def try_to_autologin
145 if cookies[autologin_cookie_name] && Setting.autologin?
151 if cookies[autologin_cookie_name] && Setting.autologin?
146 # auto-login feature starts a new session
152 # auto-login feature starts a new session
147 user = User.try_to_autologin(cookies[autologin_cookie_name])
153 user = User.try_to_autologin(cookies[autologin_cookie_name])
148 if user
154 if user
149 reset_session
155 reset_session
150 start_user_session(user)
156 start_user_session(user)
151 end
157 end
152 user
158 user
153 end
159 end
154 end
160 end
155
161
156 # Sets the logged in user
162 # Sets the logged in user
157 def logged_user=(user)
163 def logged_user=(user)
158 reset_session
164 reset_session
159 if user && user.is_a?(User)
165 if user && user.is_a?(User)
160 User.current = user
166 User.current = user
161 start_user_session(user)
167 start_user_session(user)
162 else
168 else
163 User.current = User.anonymous
169 User.current = User.anonymous
164 end
170 end
165 end
171 end
166
172
167 # Logs out current user
173 # Logs out current user
168 def logout_user
174 def logout_user
169 if User.current.logged?
175 if User.current.logged?
170 cookies.delete(autologin_cookie_name)
176 cookies.delete(autologin_cookie_name)
171 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
177 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin'])
172 self.logged_user = nil
178 self.logged_user = nil
173 end
179 end
174 end
180 end
175
181
176 # check if login is globally required to access the application
182 # check if login is globally required to access the application
177 def check_if_login_required
183 def check_if_login_required
178 # no check needed if user is already logged in
184 # no check needed if user is already logged in
179 return true if User.current.logged?
185 return true if User.current.logged?
180 require_login if Setting.login_required?
186 require_login if Setting.login_required?
181 end
187 end
182
188
183 def check_password_change
189 def check_password_change
184 if session[:pwd]
190 if session[:pwd]
185 if User.current.must_change_password?
191 if User.current.must_change_password?
186 redirect_to my_password_path
192 redirect_to my_password_path
187 else
193 else
188 session.delete(:pwd)
194 session.delete(:pwd)
189 end
195 end
190 end
196 end
191 end
197 end
192
198
193 def set_localization
199 def set_localization
194 lang = nil
200 lang = nil
195 if User.current.logged?
201 if User.current.logged?
196 lang = find_language(User.current.language)
202 lang = find_language(User.current.language)
197 end
203 end
198 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
204 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
199 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
205 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first
200 if !accept_lang.blank?
206 if !accept_lang.blank?
201 accept_lang = accept_lang.downcase
207 accept_lang = accept_lang.downcase
202 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
208 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
203 end
209 end
204 end
210 end
205 lang ||= Setting.default_language
211 lang ||= Setting.default_language
206 set_language_if_valid(lang)
212 set_language_if_valid(lang)
207 end
213 end
208
214
209 def require_login
215 def require_login
210 if !User.current.logged?
216 if !User.current.logged?
211 # Extract only the basic url parameters on non-GET requests
217 # Extract only the basic url parameters on non-GET requests
212 if request.get?
218 if request.get?
213 url = url_for(params)
219 url = url_for(params)
214 else
220 else
215 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
221 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
216 end
222 end
217 respond_to do |format|
223 respond_to do |format|
218 format.html {
224 format.html {
219 if request.xhr?
225 if request.xhr?
220 head :unauthorized
226 head :unauthorized
221 else
227 else
222 redirect_to :controller => "account", :action => "login", :back_url => url
228 redirect_to :controller => "account", :action => "login", :back_url => url
223 end
229 end
224 }
230 }
225 format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
231 format.atom { redirect_to :controller => "account", :action => "login", :back_url => url }
226 format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
232 format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
227 format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
233 format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
228 format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
234 format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' }
229 end
235 end
230 return false
236 return false
231 end
237 end
232 true
238 true
233 end
239 end
234
240
235 def require_admin
241 def require_admin
236 return unless require_login
242 return unless require_login
237 if !User.current.admin?
243 if !User.current.admin?
238 render_403
244 render_403
239 return false
245 return false
240 end
246 end
241 true
247 true
242 end
248 end
243
249
244 def deny_access
250 def deny_access
245 User.current.logged? ? render_403 : require_login
251 User.current.logged? ? render_403 : require_login
246 end
252 end
247
253
248 # Authorize the user for the requested action
254 # Authorize the user for the requested action
249 def authorize(ctrl = params[:controller], action = params[:action], global = false)
255 def authorize(ctrl = params[:controller], action = params[:action], global = false)
250 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
256 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project || @projects, :global => global)
251 if allowed
257 if allowed
252 true
258 true
253 else
259 else
254 if @project && @project.archived?
260 if @project && @project.archived?
255 render_403 :message => :notice_not_authorized_archived_project
261 render_403 :message => :notice_not_authorized_archived_project
256 else
262 else
257 deny_access
263 deny_access
258 end
264 end
259 end
265 end
260 end
266 end
261
267
262 # Authorize the user for the requested action outside a project
268 # Authorize the user for the requested action outside a project
263 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
269 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
264 authorize(ctrl, action, global)
270 authorize(ctrl, action, global)
265 end
271 end
266
272
267 # Find project of id params[:id]
273 # Find project of id params[:id]
268 def find_project
274 def find_project
269 @project = Project.find(params[:id])
275 @project = Project.find(params[:id])
270 rescue ActiveRecord::RecordNotFound
276 rescue ActiveRecord::RecordNotFound
271 render_404
277 render_404
272 end
278 end
273
279
274 # Find project of id params[:project_id]
280 # Find project of id params[:project_id]
275 def find_project_by_project_id
281 def find_project_by_project_id
276 @project = Project.find(params[:project_id])
282 @project = Project.find(params[:project_id])
277 rescue ActiveRecord::RecordNotFound
283 rescue ActiveRecord::RecordNotFound
278 render_404
284 render_404
279 end
285 end
280
286
281 # Find a project based on params[:project_id]
287 # Find a project based on params[:project_id]
282 # TODO: some subclasses override this, see about merging their logic
288 # TODO: some subclasses override this, see about merging their logic
283 def find_optional_project
289 def find_optional_project
284 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
290 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
285 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
291 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
286 allowed ? true : deny_access
292 allowed ? true : deny_access
287 rescue ActiveRecord::RecordNotFound
293 rescue ActiveRecord::RecordNotFound
288 render_404
294 render_404
289 end
295 end
290
296
291 # Finds and sets @project based on @object.project
297 # Finds and sets @project based on @object.project
292 def find_project_from_association
298 def find_project_from_association
293 render_404 unless @object.present?
299 render_404 unless @object.present?
294
300
295 @project = @object.project
301 @project = @object.project
296 end
302 end
297
303
298 def find_model_object
304 def find_model_object
299 model = self.class.model_object
305 model = self.class.model_object
300 if model
306 if model
301 @object = model.find(params[:id])
307 @object = model.find(params[:id])
302 self.instance_variable_set('@' + controller_name.singularize, @object) if @object
308 self.instance_variable_set('@' + controller_name.singularize, @object) if @object
303 end
309 end
304 rescue ActiveRecord::RecordNotFound
310 rescue ActiveRecord::RecordNotFound
305 render_404
311 render_404
306 end
312 end
307
313
308 def self.model_object(model)
314 def self.model_object(model)
309 self.model_object = model
315 self.model_object = model
310 end
316 end
311
317
312 # Find the issue whose id is the :id parameter
318 # Find the issue whose id is the :id parameter
313 # Raises a Unauthorized exception if the issue is not visible
319 # Raises a Unauthorized exception if the issue is not visible
314 def find_issue
320 def find_issue
315 # Issue.visible.find(...) can not be used to redirect user to the login form
321 # Issue.visible.find(...) can not be used to redirect user to the login form
316 # if the issue actually exists but requires authentication
322 # if the issue actually exists but requires authentication
317 @issue = Issue.find(params[:id])
323 @issue = Issue.find(params[:id])
318 raise Unauthorized unless @issue.visible?
324 raise Unauthorized unless @issue.visible?
319 @project = @issue.project
325 @project = @issue.project
320 rescue ActiveRecord::RecordNotFound
326 rescue ActiveRecord::RecordNotFound
321 render_404
327 render_404
322 end
328 end
323
329
324 # Find issues with a single :id param or :ids array param
330 # Find issues with a single :id param or :ids array param
325 # Raises a Unauthorized exception if one of the issues is not visible
331 # Raises a Unauthorized exception if one of the issues is not visible
326 def find_issues
332 def find_issues
327 @issues = Issue.where(:id => (params[:id] || params[:ids])).preload(:project, :status, :tracker, :priority, :author, :assigned_to, :relations_to).to_a
333 @issues = Issue.where(:id => (params[:id] || params[:ids])).preload(:project, :status, :tracker, :priority, :author, :assigned_to, :relations_to).to_a
328 raise ActiveRecord::RecordNotFound if @issues.empty?
334 raise ActiveRecord::RecordNotFound if @issues.empty?
329 raise Unauthorized unless @issues.all?(&:visible?)
335 raise Unauthorized unless @issues.all?(&:visible?)
330 @projects = @issues.collect(&:project).compact.uniq
336 @projects = @issues.collect(&:project).compact.uniq
331 @project = @projects.first if @projects.size == 1
337 @project = @projects.first if @projects.size == 1
332 rescue ActiveRecord::RecordNotFound
338 rescue ActiveRecord::RecordNotFound
333 render_404
339 render_404
334 end
340 end
335
341
336 def find_attachments
342 def find_attachments
337 if (attachments = params[:attachments]).present?
343 if (attachments = params[:attachments]).present?
338 att = attachments.values.collect do |attachment|
344 att = attachments.values.collect do |attachment|
339 Attachment.find_by_token( attachment[:token] ) if attachment[:token].present?
345 Attachment.find_by_token( attachment[:token] ) if attachment[:token].present?
340 end
346 end
341 att.compact!
347 att.compact!
342 end
348 end
343 @attachments = att || []
349 @attachments = att || []
344 end
350 end
345
351
346 # make sure that the user is a member of the project (or admin) if project is private
352 # make sure that the user is a member of the project (or admin) if project is private
347 # used as a before_filter for actions that do not require any particular permission on the project
353 # used as a before_filter for actions that do not require any particular permission on the project
348 def check_project_privacy
354 def check_project_privacy
349 if @project && !@project.archived?
355 if @project && !@project.archived?
350 if @project.visible?
356 if @project.visible?
351 true
357 true
352 else
358 else
353 deny_access
359 deny_access
354 end
360 end
355 else
361 else
356 @project = nil
362 @project = nil
357 render_404
363 render_404
358 false
364 false
359 end
365 end
360 end
366 end
361
367
362 def back_url
368 def back_url
363 url = params[:back_url]
369 url = params[:back_url]
364 if url.nil? && referer = request.env['HTTP_REFERER']
370 if url.nil? && referer = request.env['HTTP_REFERER']
365 url = CGI.unescape(referer.to_s)
371 url = CGI.unescape(referer.to_s)
366 end
372 end
367 url
373 url
368 end
374 end
369
375
370 def redirect_back_or_default(default)
376 def redirect_back_or_default(default)
371 back_url = params[:back_url].to_s
377 back_url = params[:back_url].to_s
372 if back_url.present?
378 if back_url.present?
373 begin
379 begin
374 uri = URI.parse(back_url)
380 uri = URI.parse(back_url)
375 # do not redirect user to another host or to the login or register page
381 # do not redirect user to another host or to the login or register page
376 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
382 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
377 redirect_to(back_url)
383 redirect_to(back_url)
378 return
384 return
379 end
385 end
380 rescue URI::InvalidURIError
386 rescue URI::InvalidURIError
381 logger.warn("Could not redirect to invalid URL #{back_url}")
387 logger.warn("Could not redirect to invalid URL #{back_url}")
382 # redirect to default
388 # redirect to default
383 end
389 end
384 end
390 end
385 redirect_to default
391 redirect_to default
386 false
392 false
387 end
393 end
388
394
389 # Redirects to the request referer if present, redirects to args or call block otherwise.
395 # Redirects to the request referer if present, redirects to args or call block otherwise.
390 def redirect_to_referer_or(*args, &block)
396 def redirect_to_referer_or(*args, &block)
391 redirect_to :back
397 redirect_to :back
392 rescue ::ActionController::RedirectBackError
398 rescue ::ActionController::RedirectBackError
393 if args.any?
399 if args.any?
394 redirect_to *args
400 redirect_to *args
395 elsif block_given?
401 elsif block_given?
396 block.call
402 block.call
397 else
403 else
398 raise "#redirect_to_referer_or takes arguments or a block"
404 raise "#redirect_to_referer_or takes arguments or a block"
399 end
405 end
400 end
406 end
401
407
402 def render_403(options={})
408 def render_403(options={})
403 @project = nil
409 @project = nil
404 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
410 render_error({:message => :notice_not_authorized, :status => 403}.merge(options))
405 return false
411 return false
406 end
412 end
407
413
408 def render_404(options={})
414 def render_404(options={})
409 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
415 render_error({:message => :notice_file_not_found, :status => 404}.merge(options))
410 return false
416 return false
411 end
417 end
412
418
413 # Renders an error response
419 # Renders an error response
414 def render_error(arg)
420 def render_error(arg)
415 arg = {:message => arg} unless arg.is_a?(Hash)
421 arg = {:message => arg} unless arg.is_a?(Hash)
416
422
417 @message = arg[:message]
423 @message = arg[:message]
418 @message = l(@message) if @message.is_a?(Symbol)
424 @message = l(@message) if @message.is_a?(Symbol)
419 @status = arg[:status] || 500
425 @status = arg[:status] || 500
420
426
421 respond_to do |format|
427 respond_to do |format|
422 format.html {
428 format.html {
423 render :template => 'common/error', :layout => use_layout, :status => @status
429 render :template => 'common/error', :layout => use_layout, :status => @status
424 }
430 }
425 format.any { head @status }
431 format.any { head @status }
426 end
432 end
427 end
433 end
428
434
429 # Handler for ActionView::MissingTemplate exception
435 # Handler for ActionView::MissingTemplate exception
430 def missing_template
436 def missing_template
431 logger.warn "Missing template, responding with 404"
437 logger.warn "Missing template, responding with 404"
432 @project = nil
438 @project = nil
433 render_404
439 render_404
434 end
440 end
435
441
436 # Filter for actions that provide an API response
442 # Filter for actions that provide an API response
437 # but have no HTML representation for non admin users
443 # but have no HTML representation for non admin users
438 def require_admin_or_api_request
444 def require_admin_or_api_request
439 return true if api_request?
445 return true if api_request?
440 if User.current.admin?
446 if User.current.admin?
441 true
447 true
442 elsif User.current.logged?
448 elsif User.current.logged?
443 render_error(:status => 406)
449 render_error(:status => 406)
444 else
450 else
445 deny_access
451 deny_access
446 end
452 end
447 end
453 end
448
454
449 # Picks which layout to use based on the request
455 # Picks which layout to use based on the request
450 #
456 #
451 # @return [boolean, string] name of the layout to use or false for no layout
457 # @return [boolean, string] name of the layout to use or false for no layout
452 def use_layout
458 def use_layout
453 request.xhr? ? false : 'base'
459 request.xhr? ? false : 'base'
454 end
460 end
455
461
456 def render_feed(items, options={})
462 def render_feed(items, options={})
457 @items = items || []
463 @items = items || []
458 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
464 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
459 @items = @items.slice(0, Setting.feeds_limit.to_i)
465 @items = @items.slice(0, Setting.feeds_limit.to_i)
460 @title = options[:title] || Setting.app_title
466 @title = options[:title] || Setting.app_title
461 render :template => "common/feed", :formats => [:atom], :layout => false,
467 render :template => "common/feed", :formats => [:atom], :layout => false,
462 :content_type => 'application/atom+xml'
468 :content_type => 'application/atom+xml'
463 end
469 end
464
470
465 def self.accept_rss_auth(*actions)
471 def self.accept_rss_auth(*actions)
466 if actions.any?
472 if actions.any?
467 self.accept_rss_auth_actions = actions
473 self.accept_rss_auth_actions = actions
468 else
474 else
469 self.accept_rss_auth_actions || []
475 self.accept_rss_auth_actions || []
470 end
476 end
471 end
477 end
472
478
473 def accept_rss_auth?(action=action_name)
479 def accept_rss_auth?(action=action_name)
474 self.class.accept_rss_auth.include?(action.to_sym)
480 self.class.accept_rss_auth.include?(action.to_sym)
475 end
481 end
476
482
477 def self.accept_api_auth(*actions)
483 def self.accept_api_auth(*actions)
478 if actions.any?
484 if actions.any?
479 self.accept_api_auth_actions = actions
485 self.accept_api_auth_actions = actions
480 else
486 else
481 self.accept_api_auth_actions || []
487 self.accept_api_auth_actions || []
482 end
488 end
483 end
489 end
484
490
485 def accept_api_auth?(action=action_name)
491 def accept_api_auth?(action=action_name)
486 self.class.accept_api_auth.include?(action.to_sym)
492 self.class.accept_api_auth.include?(action.to_sym)
487 end
493 end
488
494
489 # Returns the number of objects that should be displayed
495 # Returns the number of objects that should be displayed
490 # on the paginated list
496 # on the paginated list
491 def per_page_option
497 def per_page_option
492 per_page = nil
498 per_page = nil
493 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
499 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
494 per_page = params[:per_page].to_s.to_i
500 per_page = params[:per_page].to_s.to_i
495 session[:per_page] = per_page
501 session[:per_page] = per_page
496 elsif session[:per_page]
502 elsif session[:per_page]
497 per_page = session[:per_page]
503 per_page = session[:per_page]
498 else
504 else
499 per_page = Setting.per_page_options_array.first || 25
505 per_page = Setting.per_page_options_array.first || 25
500 end
506 end
501 per_page
507 per_page
502 end
508 end
503
509
504 # Returns offset and limit used to retrieve objects
510 # Returns offset and limit used to retrieve objects
505 # for an API response based on offset, limit and page parameters
511 # for an API response based on offset, limit and page parameters
506 def api_offset_and_limit(options=params)
512 def api_offset_and_limit(options=params)
507 if options[:offset].present?
513 if options[:offset].present?
508 offset = options[:offset].to_i
514 offset = options[:offset].to_i
509 if offset < 0
515 if offset < 0
510 offset = 0
516 offset = 0
511 end
517 end
512 end
518 end
513 limit = options[:limit].to_i
519 limit = options[:limit].to_i
514 if limit < 1
520 if limit < 1
515 limit = 25
521 limit = 25
516 elsif limit > 100
522 elsif limit > 100
517 limit = 100
523 limit = 100
518 end
524 end
519 if offset.nil? && options[:page].present?
525 if offset.nil? && options[:page].present?
520 offset = (options[:page].to_i - 1) * limit
526 offset = (options[:page].to_i - 1) * limit
521 offset = 0 if offset < 0
527 offset = 0 if offset < 0
522 end
528 end
523 offset ||= 0
529 offset ||= 0
524
530
525 [offset, limit]
531 [offset, limit]
526 end
532 end
527
533
528 # qvalues http header parser
534 # qvalues http header parser
529 # code taken from webrick
535 # code taken from webrick
530 def parse_qvalues(value)
536 def parse_qvalues(value)
531 tmp = []
537 tmp = []
532 if value
538 if value
533 parts = value.split(/,\s*/)
539 parts = value.split(/,\s*/)
534 parts.each {|part|
540 parts.each {|part|
535 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
541 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
536 val = m[1]
542 val = m[1]
537 q = (m[2] or 1).to_f
543 q = (m[2] or 1).to_f
538 tmp.push([val, q])
544 tmp.push([val, q])
539 end
545 end
540 }
546 }
541 tmp = tmp.sort_by{|val, q| -q}
547 tmp = tmp.sort_by{|val, q| -q}
542 tmp.collect!{|val, q| val}
548 tmp.collect!{|val, q| val}
543 end
549 end
544 return tmp
550 return tmp
545 rescue
551 rescue
546 nil
552 nil
547 end
553 end
548
554
549 # Returns a string that can be used as filename value in Content-Disposition header
555 # Returns a string that can be used as filename value in Content-Disposition header
550 def filename_for_content_disposition(name)
556 def filename_for_content_disposition(name)
551 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
557 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
552 end
558 end
553
559
554 def api_request?
560 def api_request?
555 %w(xml json).include? params[:format]
561 %w(xml json).include? params[:format]
556 end
562 end
557
563
558 # Returns the API key present in the request
564 # Returns the API key present in the request
559 def api_key_from_request
565 def api_key_from_request
560 if params[:key].present?
566 if params[:key].present?
561 params[:key].to_s
567 params[:key].to_s
562 elsif request.headers["X-Redmine-API-Key"].present?
568 elsif request.headers["X-Redmine-API-Key"].present?
563 request.headers["X-Redmine-API-Key"].to_s
569 request.headers["X-Redmine-API-Key"].to_s
564 end
570 end
565 end
571 end
566
572
567 # Returns the API 'switch user' value if present
573 # Returns the API 'switch user' value if present
568 def api_switch_user_from_request
574 def api_switch_user_from_request
569 request.headers["X-Redmine-Switch-User"].to_s.presence
575 request.headers["X-Redmine-Switch-User"].to_s.presence
570 end
576 end
571
577
572 # Renders a warning flash if obj has unsaved attachments
578 # Renders a warning flash if obj has unsaved attachments
573 def render_attachment_warning_if_needed(obj)
579 def render_attachment_warning_if_needed(obj)
574 flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
580 flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
575 end
581 end
576
582
577 # Rescues an invalid query statement. Just in case...
583 # Rescues an invalid query statement. Just in case...
578 def query_statement_invalid(exception)
584 def query_statement_invalid(exception)
579 logger.error "Query::StatementInvalid: #{exception.message}" if logger
585 logger.error "Query::StatementInvalid: #{exception.message}" if logger
580 session.delete(:query)
586 session.delete(:query)
581 sort_clear if respond_to?(:sort_clear)
587 sort_clear if respond_to?(:sort_clear)
582 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
588 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
583 end
589 end
584
590
585 # Renders a 200 response for successfull updates or deletions via the API
591 # Renders a 200 response for successfull updates or deletions via the API
586 def render_api_ok
592 def render_api_ok
587 render_api_head :ok
593 render_api_head :ok
588 end
594 end
589
595
590 # Renders a head API response
596 # Renders a head API response
591 def render_api_head(status)
597 def render_api_head(status)
592 # #head would return a response body with one space
598 # #head would return a response body with one space
593 render :text => '', :status => status, :layout => nil
599 render :text => '', :status => status, :layout => nil
594 end
600 end
595
601
596 # Renders API response on validation failure
602 # Renders API response on validation failure
597 def render_validation_errors(objects)
603 def render_validation_errors(objects)
598 if objects.is_a?(Array)
604 if objects.is_a?(Array)
599 @error_messages = objects.map {|object| object.errors.full_messages}.flatten
605 @error_messages = objects.map {|object| object.errors.full_messages}.flatten
600 else
606 else
601 @error_messages = objects.errors.full_messages
607 @error_messages = objects.errors.full_messages
602 end
608 end
603 render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => nil
609 render :template => 'common/error_messages.api', :status => :unprocessable_entity, :layout => nil
604 end
610 end
605
611
606 # Overrides #_include_layout? so that #render with no arguments
612 # Overrides #_include_layout? so that #render with no arguments
607 # doesn't use the layout for api requests
613 # doesn't use the layout for api requests
608 def _include_layout?(*args)
614 def _include_layout?(*args)
609 api_request? ? false : super
615 api_request? ? false : super
610 end
616 end
611 end
617 end
General Comments 0
You need to be logged in to leave comments. Login now