2019-03-16 09:37:35 +00:00
# frozen_string_literal: true
2019-03-15 01:32:57 +00:00
2011-04-11 17:53:15 +00:00
# Redmine - project management software
2023-01-01 06:19:35 +00:00
# Copyright (C) 2006-2023 Jean-Philippe Lang
2007-03-12 17:59:02 +00:00
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
2011-05-12 04:25:47 +00:00
#
2007-03-12 17:59:02 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
2011-05-12 04:25:47 +00:00
#
2007-03-12 17:59:02 +00:00
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2008-07-26 08:46:33 +00:00
require 'uri'
2008-09-21 12:45:22 +00:00
require 'cgi'
2008-07-26 08:46:33 +00:00
2019-05-21 23:53:10 +00:00
class Unauthorized < StandardError ; end
2011-07-03 11:01:08 +00:00
2007-03-12 17:59:02 +00:00
class ApplicationController < ActionController :: Base
2009-02-21 11:04:50 +00:00
include Redmine :: I18n
2012-12-17 18:21:24 +00:00
include Redmine :: Pagination
2015-10-19 17:30:28 +00:00
include Redmine :: Hook :: Helper
2012-12-23 15:18:39 +00:00
include RoutesHelper
2019-05-19 22:27:41 +00:00
include AvatarsHelper
2012-12-23 15:18:39 +00:00
helper :routes
2019-05-19 22:27:41 +00:00
helper :avatars
2012-10-05 00:30:52 +00:00
2012-04-25 17:17:49 +00:00
class_attribute :accept_api_auth_actions
2022-03-19 09:56:46 +00:00
class_attribute :accept_atom_auth_actions
2012-04-25 17:17:49 +00:00
class_attribute :model_object
2009-09-13 17:14:35 +00:00
2008-08-10 15:22:54 +00:00
layout 'base'
2011-08-22 13:29:48 +00:00
2013-11-22 22:57:30 +00:00
def verify_authenticity_token
unless api_request?
super
end
end
2011-07-25 21:15:09 +00:00
def handle_unverified_request
2013-11-22 22:57:30 +00:00
unless api_request?
2022-01-22 08:43:42 +00:00
begin
super
rescue ActionController :: InvalidAuthenticityToken = > e
logger . error ( " ActionController::InvalidAuthenticityToken: #{ e . message } " ) if logger
ensure
cookies . delete ( autologin_cookie_name )
self . logged_user = nil
set_localization
render_error :status = > 422 , :message = > l ( :error_invalid_authenticity_token )
end
2013-11-11 20:59:17 +00:00
end
2011-07-25 21:15:09 +00:00
end
2011-05-12 04:25:47 +00:00
2020-08-29 06:37:09 +00:00
before_action :session_expiration , :user_setup , :check_if_login_required , :set_localization , :check_password_change , :check_twofa_activation
2019-05-20 22:26:30 +00:00
after_action :record_project_usage
2011-05-12 04:25:47 +00:00
2011-07-03 11:01:08 +00:00
rescue_from :: Unauthorized , :with = > :deny_access
2012-08-13 18:34:00 +00:00
rescue_from :: ActionView :: MissingTemplate , :with = > :missing_template
2011-05-12 04:25:47 +00:00
2009-10-21 17:07:18 +00:00
include Redmine :: Search :: Controller
2008-01-19 11:53:43 +00:00
include Redmine :: MenuManager :: MenuController
helper Redmine :: MenuManager :: MenuHelper
2011-05-12 04:25:47 +00:00
2015-06-19 18:41:10 +00:00
include Redmine :: SudoMode :: Controller
2012-06-10 13:16:56 +00:00
def session_expiration
2015-10-24 10:15:22 +00:00
if session [ :user_id ] && Rails . application . config . redmine_verify_sessions != false
2012-06-10 13:16:56 +00:00
if session_expired? && ! try_to_autologin
2014-09-13 10:35:46 +00:00
set_localization ( User . active . find_by_id ( session [ :user_id ] ) )
2015-05-09 10:43:09 +00:00
self . logged_user = nil
2012-06-10 13:16:56 +00:00
flash [ :error ] = l ( :error_session_expired )
2015-05-09 10:43:09 +00:00
require_login
2012-06-10 13:16:56 +00:00
end
end
end
def session_expired?
2015-10-24 10:15:22 +00:00
! User . verify_session_token ( session [ :user_id ] , session [ :tk ] )
2012-06-10 13:16:56 +00:00
end
def start_user_session ( user )
session [ :user_id ] = user . id
2015-10-24 10:15:22 +00:00
session [ :tk ] = user . generate_session_token
2013-08-05 17:58:33 +00:00
if user . must_change_password?
session [ :pwd ] = '1'
end
2020-08-29 06:37:09 +00:00
if user . must_activate_twofa?
session [ :must_activate_twofa ] = '1'
end
2012-06-10 13:16:56 +00:00
end
2007-08-29 16:52:35 +00:00
def user_setup
2007-12-30 10:51:34 +00:00
# Check the settings cache for each request
2007-08-31 17:45:32 +00:00
Setting . check_cache
2007-12-30 10:51:34 +00:00
# Find the current user
2009-06-06 10:20:27 +00:00
User . current = find_current_user
2012-09-09 14:54:22 +00:00
logger . info ( " Current user: " + ( User . current . logged? ? " #{ User . current . login } (id= #{ User . current . id } ) " : " anonymous " ) ) if logger
2007-12-30 10:51:34 +00:00
end
2011-05-12 04:25:47 +00:00
2007-12-30 10:51:34 +00:00
# Returns the current user or nil if no user is logged in
2009-06-06 10:20:27 +00:00
# and starts a session if needed
2007-12-30 10:51:34 +00:00
def find_current_user
2012-07-27 18:24:42 +00:00
user = nil
unless api_request?
2012-10-05 00:30:52 +00:00
if session [ :user_id ]
2012-07-27 18:24:42 +00:00
# existing session
2020-07-08 17:29:56 +00:00
user =
begin
User . active . find ( session [ :user_id ] )
rescue
nil
end
2012-07-27 18:24:42 +00:00
elsif autologin_user = try_to_autologin
user = autologin_user
2022-03-19 09:56:46 +00:00
elsif params [ :format ] == 'atom' && params [ :key ] && request . get? && accept_atom_auth?
# ATOM key authentication does not start a session
user = User . find_by_atom_key ( params [ :key ] )
2012-07-27 18:24:42 +00:00
end
end
if user . nil? && Setting . rest_api_enabled? && accept_api_auth?
2011-07-09 08:56:07 +00:00
if ( key = api_key_from_request )
2009-12-23 06:27:38 +00:00
# Use API key
2012-07-27 18:24:42 +00:00
user = User . find_by_api_key ( key )
2019-03-27 02:15:24 +00:00
elsif / \ ABasic /i . match? ( request . authorization . to_s )
2009-12-23 06:27:38 +00:00
# HTTP Basic, either username/password or API key/random
authenticate_with_http_basic do | username , password |
2022-02-24 19:10:35 +00:00
user = User . try_to_login ( username , password )
# Don't allow using username/password when two-factor auth is active
if user & . twofa_active?
render_error :message = > 'HTTP Basic authentication is not allowed. Use API key instead' , :status = > 401
return
end
user || = User . find_by_api_key ( username )
2009-12-23 06:27:38 +00:00
end
2013-08-05 17:58:33 +00:00
if user && user . must_change_password?
render_error :message = > 'You must change your password' , :status = > 403
return
end
2009-12-23 06:27:38 +00:00
end
2012-10-11 17:07:24 +00:00
# Switch user if requested by an admin user
if user && user . admin? && ( username = api_switch_user_from_request )
su = User . find_by_login ( username )
if su && su . active?
logger . info ( " User switched by: #{ user . login } (id= #{ user . id } ) " ) if logger
user = su
else
render_error :message = > 'Invalid X-Redmine-Switch-User header' , :status = > 412
end
end
2007-03-12 17:59:02 +00:00
end
2016-02-05 07:33:24 +00:00
# store current ip address in user object ephemerally
user . remote_ip = request . remote_ip if user
2012-07-27 18:24:42 +00:00
user
2007-03-12 17:59:02 +00:00
end
2009-12-23 06:27:38 +00:00
2013-03-02 20:28:21 +00:00
def autologin_cookie_name
Redmine :: Configuration [ 'autologin_cookie_name' ] . presence || 'autologin'
end
2012-06-10 13:16:56 +00:00
def try_to_autologin
2013-03-02 20:28:21 +00:00
if cookies [ autologin_cookie_name ] && Setting . autologin?
2012-06-10 13:16:56 +00:00
# auto-login feature starts a new session
2013-03-02 20:28:21 +00:00
user = User . try_to_autologin ( cookies [ autologin_cookie_name ] )
2012-06-10 13:16:56 +00:00
if user
reset_session
start_user_session ( user )
end
user
end
end
2009-02-25 14:59:33 +00:00
# Sets the logged in user
def logged_user = ( user )
2009-11-21 10:02:39 +00:00
reset_session
2009-02-25 14:59:33 +00:00
if user && user . is_a? ( User )
User . current = user
2012-06-10 13:16:56 +00:00
start_user_session ( user )
2009-02-25 14:59:33 +00:00
else
User . current = User . anonymous
end
end
2011-05-12 04:25:47 +00:00
2012-04-15 14:31:54 +00:00
# Logs out current user
def logout_user
if User . current . logged?
2017-04-04 17:15:07 +00:00
if autologin = cookies . delete ( autologin_cookie_name )
User . current . delete_autologin_token ( autologin )
end
User . current . delete_session_token ( session [ :tk ] )
2012-04-15 14:31:54 +00:00
self . logged_user = nil
end
end
2007-03-12 17:59:02 +00:00
# check if login is globally required to access the application
def check_if_login_required
2007-05-06 12:49:32 +00:00
# no check needed if user is already logged in
2007-08-29 16:52:35 +00:00
return true if User . current . logged?
2020-09-17 15:49:06 +00:00
2007-03-12 17:59:02 +00:00
require_login if Setting . login_required?
2011-05-12 04:25:47 +00:00
end
2013-08-05 17:58:33 +00:00
def check_password_change
if session [ :pwd ]
if User . current . must_change_password?
2015-05-10 10:26:55 +00:00
flash [ :error ] = l ( :error_password_expired )
2013-08-05 17:58:33 +00:00
redirect_to my_password_path
else
session . delete ( :pwd )
end
end
end
2020-08-29 06:37:09 +00:00
def init_twofa_pairing_and_send_code_for ( twofa )
twofa . init_pairing!
if twofa . send_code ( controller : 'twofa' , action : 'activate' )
flash [ :notice ] = l ( 'twofa_code_sent' )
end
redirect_to controller : 'twofa' , action : 'activate_confirm' , scheme : twofa . scheme_name
end
def check_twofa_activation
if session [ :must_activate_twofa ]
if User . current . must_activate_twofa?
flash [ :warning ] = l ( 'twofa_warning_require' )
if Redmine :: Twofa . available_schemes . length == 1
twofa_scheme = Redmine :: Twofa . for_twofa_scheme ( Redmine :: Twofa . available_schemes . first )
twofa = twofa_scheme . new ( User . current )
init_twofa_pairing_and_send_code_for ( twofa )
else
redirect_to controller : 'twofa' , action : 'select_scheme'
end
else
session . delete ( :must_activate_twofa )
end
end
end
2014-09-13 10:35:46 +00:00
def set_localization ( user = User . current )
2009-02-21 11:04:50 +00:00
lang = nil
2014-09-13 10:35:46 +00:00
if user && user . logged?
lang = find_language ( user . language )
2009-02-21 11:04:50 +00:00
end
2014-01-24 10:28:12 +00:00
if lang . nil? && ! Setting . force_default_language_for_anonymous? && request . env [ 'HTTP_ACCEPT_LANGUAGE' ]
2010-04-30 10:33:25 +00:00
accept_lang = parse_qvalues ( request . env [ 'HTTP_ACCEPT_LANGUAGE' ] ) . first
2009-02-21 11:04:50 +00:00
if ! accept_lang . blank?
2010-04-30 10:33:25 +00:00
accept_lang = accept_lang . downcase
2009-02-21 11:04:50 +00:00
lang = find_language ( accept_lang ) || find_language ( accept_lang . split ( '-' ) . first )
2007-03-12 17:59:02 +00:00
end
2009-02-21 11:04:50 +00:00
end
lang || = Setting . default_language
set_language_if_valid ( lang )
2007-03-12 17:59:02 +00:00
end
2011-05-12 04:25:47 +00:00
2007-03-12 17:59:02 +00:00
def require_login
2007-08-29 16:52:35 +00:00
if ! User . current . logged?
2009-11-14 19:41:02 +00:00
# Extract only the basic url parameters on non-GET requests
if request . get?
2016-07-14 07:43:34 +00:00
url = request . original_url
2009-11-14 19:41:02 +00:00
else
url = url_for ( :controller = > params [ :controller ] , :action = > params [ :action ] , :id = > params [ :id ] , :project_id = > params [ :project_id ] )
end
2009-12-23 06:27:33 +00:00
respond_to do | format |
2020-10-11 12:57:39 +00:00
format . html do
2013-06-12 16:49:12 +00:00
if request . xhr?
head :unauthorized
else
2015-03-10 18:22:53 +00:00
redirect_to signin_path ( :back_url = > url )
2013-06-12 16:49:12 +00:00
end
2020-10-11 12:57:39 +00:00
end
format . any ( :atom , :pdf , :csv ) do
2015-03-10 18:22:53 +00:00
redirect_to signin_path ( :back_url = > url )
2020-10-11 12:57:39 +00:00
end
format . api do
2019-04-10 02:51:28 +00:00
if Setting . rest_api_enabled? && accept_api_auth?
head ( :unauthorized , 'WWW-Authenticate' = > 'Basic realm="Redmine API"' )
else
head ( :forbidden )
end
2020-10-11 12:57:39 +00:00
end
2020-11-17 12:41:18 +00:00
format . js { head :unauthorized , 'WWW-Authenticate' = > 'Basic realm="Redmine API"' }
format . any { head :unauthorized }
2009-12-23 06:27:33 +00:00
end
2007-03-12 17:59:02 +00:00
return false
end
true
end
def require_admin
return unless require_login
2020-09-17 15:49:06 +00:00
2007-08-29 16:52:35 +00:00
if ! User . current . admin?
2007-04-30 19:47:28 +00:00
render_403
2007-03-12 17:59:02 +00:00
return false
end
true
end
2011-05-12 04:25:47 +00:00
2008-08-31 16:34:54 +00:00
def deny_access
User . current . logged? ? render_403 : require_login
end
2007-03-12 17:59:02 +00:00
2007-08-29 16:52:35 +00:00
# Authorize the user for the requested action
2009-05-17 12:59:14 +00:00
def authorize ( ctrl = params [ :controller ] , action = params [ :action ] , global = false )
2010-10-07 05:11:28 +00:00
allowed = User . current . allowed_to? ( { :controller = > ctrl , :action = > action } , @project || @projects , :global = > global )
2010-10-23 09:48:01 +00:00
if allowed
true
else
if @project && @project . archived?
2018-12-02 11:23:10 +00:00
@archived_project = @project
2010-10-23 09:48:01 +00:00
render_403 :message = > :notice_not_authorized_archived_project
2017-06-26 21:04:34 +00:00
elsif @project && ! @project . allows_to? ( :controller = > ctrl , :action = > action )
# Project module is disabled
render_403
2010-10-23 09:48:01 +00:00
else
deny_access
end
end
2007-03-12 17:59:02 +00:00
end
2009-05-17 12:59:14 +00:00
# Authorize the user for the requested action outside a project
def authorize_global ( ctrl = params [ :controller ] , action = params [ :action ] , global = true )
authorize ( ctrl , action , global )
end
2010-02-05 16:57:02 +00:00
# Find project of id params[:id]
2017-06-26 20:51:28 +00:00
def find_project ( project_id = params [ :id ] )
@project = Project . find ( project_id )
2010-02-05 16:57:02 +00:00
rescue ActiveRecord :: RecordNotFound
render_404
end
2010-03-16 15:17:47 +00:00
2010-09-10 16:00:49 +00:00
# Find project of id params[:project_id]
def find_project_by_project_id
2017-06-26 20:51:28 +00:00
find_project ( params [ :project_id ] )
2010-09-10 16:00:49 +00:00
end
2017-06-26 20:41:12 +00:00
# Find project of id params[:id] if present
def find_optional_project_by_id
if params [ :id ] . present?
2017-06-26 20:51:28 +00:00
find_project ( params [ :id ] )
2017-06-26 20:41:12 +00:00
end
end
2010-04-30 17:24:11 +00:00
# Find a project based on params[:project_id]
2017-06-26 20:51:28 +00:00
# and authorize the user for the requested action
2010-04-30 17:24:11 +00:00
def find_optional_project
2017-06-26 20:51:28 +00:00
if params [ :project_id ] . present?
2023-01-20 03:31:41 +00:00
@project = Project . find ( params [ :project_id ] )
2017-06-26 20:51:28 +00:00
end
authorize_global
2023-01-20 03:31:41 +00:00
rescue ActiveRecord :: RecordNotFound
User . current . logged? ? render_404 : require_login
false
2010-04-30 17:24:11 +00:00
end
2010-03-16 15:17:47 +00:00
# Finds and sets @project based on @object.project
def find_project_from_association
render_404 unless @object . present?
2011-05-12 04:25:47 +00:00
2010-03-16 15:17:47 +00:00
@project = @object . project
end
2010-03-17 15:41:58 +00:00
def find_model_object
2012-04-25 17:17:49 +00:00
model = self . class . model_object
2010-03-17 15:41:58 +00:00
if model
@object = model . find ( params [ :id ] )
self . instance_variable_set ( '@' + controller_name . singularize , @object ) if @object
end
rescue ActiveRecord :: RecordNotFound
render_404
end
def self . model_object ( model )
2012-04-25 17:17:49 +00:00
self . model_object = model
2010-03-17 15:41:58 +00:00
end
2010-08-12 13:57:51 +00:00
2012-12-02 09:59:22 +00:00
# Find the issue whose id is the :id parameter
# Raises a Unauthorized exception if the issue is not visible
def find_issue
# Issue.visible.find(...) can not be used to redirect user to the login form
# if the issue actually exists but requires authentication
@issue = Issue . find ( params [ :id ] )
raise Unauthorized unless @issue . visible?
2020-09-17 15:49:06 +00:00
2012-12-02 09:59:22 +00:00
@project = @issue . project
rescue ActiveRecord :: RecordNotFound
render_404
end
# Find issues with a single :id param or :ids array param
# Raises a Unauthorized exception if one of the issues is not visible
2010-08-12 13:57:51 +00:00
def find_issues
2015-10-23 16:51:46 +00:00
@issues = Issue .
where ( :id = > ( params [ :id ] || params [ :ids ] ) ) .
2019-11-25 06:32:28 +00:00
preload ( :project , :status , :tracker , :priority ,
:author , :assigned_to , :relations_to ,
{ :custom_values = > :custom_field } ) .
2015-10-23 16:51:46 +00:00
to_a
2010-08-12 13:57:51 +00:00
raise ActiveRecord :: RecordNotFound if @issues . empty?
2012-12-02 10:25:30 +00:00
raise Unauthorized unless @issues . all? ( & :visible? )
2020-09-17 15:49:06 +00:00
2023-01-17 01:38:27 +00:00
@projects = @issues . filter_map ( & :project ) . uniq
2010-09-29 05:22:53 +00:00
@project = @projects . first if @projects . size == 1
rescue ActiveRecord :: RecordNotFound
render_404
end
2011-05-12 04:25:47 +00:00
2012-12-10 20:09:41 +00:00
def find_attachments
if ( attachments = params [ :attachments ] ) . present?
att = attachments . values . collect do | attachment |
2019-11-24 13:18:21 +00:00
Attachment . find_by_token ( attachment [ :token ] ) if attachment [ :token ] . present?
2012-12-10 20:09:41 +00:00
end
att . compact!
end
@attachments = att || [ ]
end
2016-06-15 18:24:02 +00:00
def parse_params_for_bulk_update ( params )
2020-10-11 12:56:36 +00:00
attributes = ( params || { } ) . reject { | k , v | v . blank? }
2016-06-15 18:24:02 +00:00
if custom = attributes [ :custom_field_values ]
2020-10-11 12:56:36 +00:00
custom . reject! { | k , v | v . blank? }
2021-08-05 23:41:01 +00:00
end
replace_none_values_with_blank ( attributes )
end
def replace_none_values_with_blank ( params )
2021-08-05 23:46:17 +00:00
attributes = ( params || { } )
2021-08-05 23:41:01 +00:00
attributes . each_key { | k | attributes [ k ] = '' if attributes [ k ] == 'none' }
if ( custom = attributes [ :custom_field_values ] )
custom . each_key do | k |
2016-06-15 18:24:02 +00:00
if custom [ k ] . is_a? ( Array )
custom [ k ] << '' if custom [ k ] . delete ( '__none__' )
else
custom [ k ] = '' if custom [ k ] == '__none__'
end
end
end
attributes
end
2007-03-12 17:59:02 +00:00
# make sure that the user is a member of the project (or admin) if project is private
2016-07-14 07:27:31 +00:00
# used as a before_action for actions that do not require any particular permission on the project
2007-03-12 17:59:02 +00:00
def check_project_privacy
2012-06-25 17:49:35 +00:00
if @project && ! @project . archived?
2012-02-23 15:28:49 +00:00
if @project . visible?
2008-03-12 20:50:48 +00:00
true
else
2011-11-29 18:14:12 +00:00
deny_access
2008-03-12 20:50:48 +00:00
end
else
2007-05-27 17:42:04 +00:00
@project = nil
render_404
2008-03-12 20:50:48 +00:00
false
2007-05-27 17:42:04 +00:00
end
2007-03-12 17:59:02 +00:00
end
2019-05-20 22:26:30 +00:00
def record_project_usage
if @project && @project . id && User . current . logged? && User . current . allowed_to? ( :view_project , @project )
Redmine :: ProjectJumpBox . new ( User . current ) . project_used ( @project )
end
true
end
2010-08-04 13:37:44 +00:00
def back_url
2012-08-26 10:40:09 +00:00
url = params [ :back_url ]
if url . nil? && referer = request . env [ 'HTTP_REFERER' ]
url = CGI . unescape ( referer . to_s )
2020-02-02 08:38:01 +00:00
# URLs that contains the utf8=[checkmark] parameter added by Rails are
# parsed as invalid by URI.parse so the redirect to the back URL would
# not be accepted (ApplicationController#validate_back_url would return
# false)
url . gsub! ( / ( \ ?|&)utf8= \ u2713&? / , '\1' )
2012-08-26 10:40:09 +00:00
end
url
2010-08-04 13:37:44 +00:00
end
2020-02-02 08:38:01 +00:00
helper_method :back_url
2010-08-04 13:37:44 +00:00
2014-01-25 12:35:05 +00:00
def redirect_back_or_default ( default , options = { } )
2020-02-02 08:39:22 +00:00
if back_url = validate_back_url ( params [ :back_url ] . to_s )
redirect_to ( back_url )
2014-07-05 09:02:38 +00:00
return
2014-01-25 12:35:05 +00:00
elsif options [ :referer ]
redirect_to_referer_or default
return
2006-07-09 16:30:01 +00:00
end
2008-07-26 08:46:33 +00:00
redirect_to default
2011-05-01 23:15:03 +00:00
false
2006-07-09 16:30:01 +00:00
end
2011-05-12 04:25:47 +00:00
2015-09-13 14:35:20 +00:00
# Returns a validated URL string if back_url is a valid url for redirection,
# otherwise false
def validate_back_url ( back_url )
2020-02-02 08:39:22 +00:00
return false if back_url . blank?
2014-07-05 09:02:38 +00:00
if CGI . unescape ( back_url ) . include? ( '..' )
return false
end
begin
uri = URI . parse ( back_url )
rescue URI :: InvalidURIError
return false
end
2015-09-13 14:35:20 +00:00
[ :scheme , :host , :port ] . each do | component |
if uri . send ( component ) . present? && uri . send ( component ) != request . send ( component )
return false
end
2020-09-17 15:49:06 +00:00
2015-09-13 14:35:20 +00:00
uri . send ( :" #{ component } = " , nil )
end
# Always ignore basic user:password in the URL
uri . userinfo = nil
path = uri . to_s
# Ensure that the remaining URL starts with a slash, followed by a
# non-slash character or the end
2019-03-27 02:15:24 +00:00
if ! %r{ \ A/([^/]| \ z) } . match? ( path )
2014-07-05 09:02:38 +00:00
return false
end
2019-03-27 02:15:24 +00:00
if %r{ /(login|account/register|account/lost_password) } . match? ( path )
2014-07-05 09:02:38 +00:00
return false
end
2015-09-13 14:35:20 +00:00
if relative_url_root . present? && ! path . starts_with? ( relative_url_root )
2014-07-05 09:02:38 +00:00
return false
end
2015-09-13 14:35:20 +00:00
return path
end
private :validate_back_url
2020-02-02 08:38:47 +00:00
helper_method :validate_back_url
2015-09-13 14:35:20 +00:00
def valid_back_url? ( back_url )
! ! validate_back_url ( back_url )
2014-07-05 09:02:38 +00:00
end
private :valid_back_url?
2020-02-02 08:38:47 +00:00
helper_method :valid_back_url?
2014-07-05 09:02:38 +00:00
2012-04-06 16:51:10 +00:00
# Redirects to the request referer if present, redirects to args or call block otherwise.
def redirect_to_referer_or ( * args , & block )
2017-06-03 07:51:48 +00:00
if referer = request . headers [ " Referer " ]
redirect_to referer
2012-04-06 16:51:10 +00:00
else
2017-06-03 07:51:48 +00:00
if args . any?
redirect_to * args
2023-01-11 13:20:52 +00:00
elsif block
2019-10-22 12:41:21 +00:00
yield
2017-06-03 07:51:48 +00:00
else
raise " # redirect_to_referer_or takes arguments or a block "
end
2012-04-06 16:51:10 +00:00
end
end
2010-10-23 09:48:01 +00:00
def render_403 ( options = { } )
2007-04-30 19:47:28 +00:00
@project = nil
2010-10-23 11:07:04 +00:00
render_error ( { :message = > :notice_not_authorized , :status = > 403 } . merge ( options ) )
2007-04-30 19:47:28 +00:00
return false
end
2011-05-12 04:25:47 +00:00
2010-10-23 11:07:04 +00:00
def render_404 ( options = { } )
render_error ( { :message = > :notice_file_not_found , :status = > 404 } . merge ( options ) )
2007-03-12 17:59:02 +00:00
return false
end
2011-05-12 04:25:47 +00:00
2010-10-23 11:07:04 +00:00
# Renders an error response
def render_error ( arg )
arg = { :message = > arg } unless arg . is_a? ( Hash )
2011-05-12 04:25:47 +00:00
2010-10-23 11:07:04 +00:00
@message = arg [ :message ]
@message = l ( @message ) if @message . is_a? ( Symbol )
@status = arg [ :status ] || 500
2011-05-12 04:25:47 +00:00
2010-01-13 19:29:19 +00:00
respond_to do | format |
2020-10-11 12:57:39 +00:00
format . html do
2010-10-23 11:07:04 +00:00
render :template = > 'common/error' , :layout = > use_layout , :status = > @status
2020-10-11 12:57:39 +00:00
end
2020-11-17 12:41:18 +00:00
format . any { head @status }
2010-01-13 19:29:19 +00:00
end
2008-01-23 17:25:11 +00:00
end
2012-08-13 18:34:00 +00:00
# Handler for ActionView::MissingTemplate exception
2018-01-11 12:29:52 +00:00
def missing_template ( exception )
logger . warn " Missing template, responding with 404: #{ exception } "
2012-08-13 18:34:00 +00:00
@project = nil
render_404
end
2011-11-20 14:17:43 +00:00
# Filter for actions that provide an API response
# but have no HTML representation for non admin users
def require_admin_or_api_request
return true if api_request?
2020-09-17 15:49:06 +00:00
2011-11-20 14:17:43 +00:00
if User . current . admin?
true
elsif User . current . logged?
render_error ( :status = > 406 )
else
deny_access
end
end
2010-08-19 01:01:35 +00:00
# Picks which layout to use based on the request
#
# @return [boolean, string] name of the layout to use or false for no layout
def use_layout
request . xhr? ? false : 'base'
end
2011-05-12 04:25:47 +00:00
def render_feed ( items , options = { } )
2014-10-22 17:37:16 +00:00
@items = ( items || [ ] ) . to_a
2020-10-11 12:56:36 +00:00
@items . sort! { | x , y | y . event_datetime < = > x . event_datetime }
2008-03-30 08:33:04 +00:00
@items = @items . slice ( 0 , Setting . feeds_limit . to_i )
2007-08-29 16:52:35 +00:00
@title = options [ :title ] || Setting . app_title
2012-10-04 18:15:05 +00:00
render :template = > " common/feed " , :formats = > [ :atom ] , :layout = > false ,
2011-10-04 11:07:47 +00:00
:content_type = > 'application/atom+xml'
2007-08-29 16:52:35 +00:00
end
2011-08-22 13:29:48 +00:00
2022-03-19 09:56:46 +00:00
def self . accept_atom_auth ( * actions )
2011-07-09 08:56:07 +00:00
if actions . any?
2022-03-19 09:56:46 +00:00
self . accept_atom_auth_actions = actions
2011-07-09 08:56:07 +00:00
else
2022-03-19 09:56:46 +00:00
self . accept_atom_auth_actions || [ ]
2011-07-09 08:56:07 +00:00
end
end
2011-08-22 13:29:48 +00:00
2022-03-19 09:56:46 +00:00
def self . accept_rss_auth ( * actions )
ActiveSupport :: Deprecation . warn " Application # self.accept_rss_auth is deprecated and will be removed in Redmine 6.0. Please use # self.accept_atom_auth instead. "
self . class . accept_atom_auth ( * actions )
end
def accept_atom_auth? ( action = action_name )
self . class . accept_atom_auth . include? ( action . to_sym )
end
# TODO: remove in Redmine 6.0
2011-07-09 08:56:07 +00:00
def accept_rss_auth? ( action = action_name )
2022-03-19 09:56:46 +00:00
ActiveSupport :: Deprecation . warn " Application # accept_rss_auth? is deprecated and will be removed in Redmine 6.0. Please use # accept_atom_auth? instead. "
accept_atom_auth? ( action )
2011-07-09 08:56:07 +00:00
end
2011-08-22 13:29:48 +00:00
2011-07-09 08:56:07 +00:00
def self . accept_api_auth ( * actions )
if actions . any?
2012-04-25 17:17:49 +00:00
self . accept_api_auth_actions = actions
2011-07-09 08:56:07 +00:00
else
2012-04-25 17:17:49 +00:00
self . accept_api_auth_actions || [ ]
2011-07-09 08:56:07 +00:00
end
end
2011-08-22 13:29:48 +00:00
2011-07-09 08:56:07 +00:00
def accept_api_auth? ( action = action_name )
self . class . accept_api_auth . include? ( action . to_sym )
2007-08-29 16:52:35 +00:00
end
2011-05-12 04:25:47 +00:00
2007-12-29 11:36:30 +00:00
# Returns the number of objects that should be displayed
# on the paginated list
def per_page_option
per_page = nil
if params [ :per_page ] && Setting . per_page_options_array . include? ( params [ :per_page ] . to_s . to_i )
per_page = params [ :per_page ] . to_s . to_i
session [ :per_page ] = per_page
elsif session [ :per_page ]
per_page = session [ :per_page ]
else
per_page = Setting . per_page_options_array . first || 25
end
per_page
end
2010-12-23 13:33:01 +00:00
# Returns offset and limit used to retrieve objects
# for an API response based on offset, limit and page parameters
def api_offset_and_limit ( options = params )
if options [ :offset ] . present?
offset = options [ :offset ] . to_i
2010-12-11 13:13:49 +00:00
if offset < 0
offset = 0
end
end
2010-12-23 13:33:01 +00:00
limit = options [ :limit ] . to_i
2010-12-11 13:13:49 +00:00
if limit < 1
limit = 25
elsif limit > 100
limit = 100
end
2010-12-23 13:33:01 +00:00
if offset . nil? && options [ :page ] . present?
offset = ( options [ :page ] . to_i - 1 ) * limit
offset = 0 if offset < 0
end
offset || = 0
2011-05-12 04:25:47 +00:00
2010-12-11 13:13:49 +00:00
[ offset , limit ]
end
2011-05-12 04:25:47 +00:00
2007-03-12 17:59:02 +00:00
# qvalues http header parser
# code taken from webrick
def parse_qvalues ( value )
tmp = [ ]
if value
parts = value . split ( / , \ s* / )
2020-10-11 12:57:39 +00:00
parts . each do | part |
2007-03-12 17:59:02 +00:00
if m = %r{ ^([^ \ s,]+?)(?:; \ s*q=( \ d+(?: \ . \ d+)?))?$ } . match ( part )
val = m [ 1 ]
q = ( m [ 2 ] or 1 ) . to_f
tmp . push ( [ val , q ] )
end
2020-10-11 12:57:39 +00:00
end
2007-03-12 17:59:02 +00:00
tmp = tmp . sort_by { | val , q | - q }
tmp . collect! { | val , q | val }
end
return tmp
2009-02-21 11:04:50 +00:00
rescue
nil
2007-03-12 17:59:02 +00:00
end
2011-05-12 04:25:47 +00:00
2008-01-10 22:42:41 +00:00
# Returns a string that can be used as filename value in Content-Disposition header
def filename_for_content_disposition ( name )
2021-04-01 15:19:16 +00:00
name
2008-01-10 22:42:41 +00:00
end
2011-05-12 04:25:47 +00:00
2010-01-17 20:23:06 +00:00
def api_request?
%w( xml json ) . include? params [ :format ]
end
2011-05-12 04:25:47 +00:00
2010-12-23 14:49:14 +00:00
# Returns the API key present in the request
def api_key_from_request
if params [ :key ] . present?
2012-06-10 13:23:01 +00:00
params [ :key ] . to_s
2010-12-23 14:49:14 +00:00
elsif request . headers [ " X-Redmine-API-Key " ] . present?
2012-06-10 13:23:01 +00:00
request . headers [ " X-Redmine-API-Key " ] . to_s
2010-12-23 14:49:14 +00:00
end
end
2010-03-03 17:05:00 +00:00
2012-10-11 17:07:24 +00:00
# Returns the API 'switch user' value if present
def api_switch_user_from_request
request . headers [ " X-Redmine-Switch-User " ] . to_s . presence
end
2010-03-03 17:05:00 +00:00
# Renders a warning flash if obj has unsaved attachments
def render_attachment_warning_if_needed ( obj )
flash [ :warning ] = l ( :warning_attachments_not_saved , obj . unsaved_attachments . size ) if obj . unsaved_attachments . present?
end
2010-04-29 15:19:19 +00:00
# Rescues an invalid query statement. Just in case...
def query_statement_invalid ( exception )
logger . error " Query::StatementInvalid: #{ exception . message } " if logger
2017-03-14 18:20:47 +00:00
session . delete ( :issue_query )
2021-03-25 04:39:18 +00:00
render_error l ( :error_query_statement_invalid )
2010-04-29 15:19:19 +00:00
end
2021-05-28 03:58:01 +00:00
def query_error ( exception )
Rails . logger . debug " #{ exception . class . name } : #{ exception . message } "
Rails . logger . debug " #{ exception . backtrace . join ( " \n " ) } "
render_404
end
2019-02-05 08:33:29 +00:00
# Renders a 204 response for successful updates or deletions via the API
2012-07-14 08:13:55 +00:00
def render_api_ok
2019-02-05 08:33:29 +00:00
render_api_head :no_content
2012-10-25 20:38:29 +00:00
end
# Renders a head API response
def render_api_head ( status )
2017-06-01 18:28:18 +00:00
head status
2012-07-14 08:13:55 +00:00
end
2010-12-03 11:51:06 +00:00
# Renders API response on validation failure
2014-12-21 20:15:24 +00:00
# for an object or an array of objects
2012-03-04 15:24:14 +00:00
def render_validation_errors ( objects )
2014-12-21 20:15:24 +00:00
messages = Array . wrap ( objects ) . map { | object | object . errors . full_messages } . flatten
render_api_errors ( messages )
end
def render_api_errors ( * messages )
@error_messages = messages . flatten
2021-04-01 15:36:57 +00:00
render :template = > 'common/error_messages' , :format = > [ :api ] , :status = > :unprocessable_entity , :layout = > nil
2010-12-04 17:43:39 +00:00
end
2011-05-12 04:25:47 +00:00
2012-04-25 17:17:49 +00:00
# Overrides #_include_layout? so that #render with no arguments
2010-12-04 17:43:39 +00:00
# doesn't use the layout for api requests
2012-04-25 17:17:49 +00:00
def _include_layout? ( * args )
api_request? ? false : super
2010-12-04 17:43:39 +00:00
end
2007-08-29 16:52:35 +00:00
end