2018-09-14 01:42:05 -04:00
# frozen_string_literal: true
2013-12-06 10:05:34 -05:00
require 'gon'
2015-08-04 18:21:12 -04:00
require 'fogbugz'
2013-12-06 10:05:34 -05:00
2013-08-21 05:33:12 -04:00
class ApplicationController < ActionController :: Base
2016-04-14 08:28:46 -04:00
include Gitlab :: GonHelper
2015-09-09 08:37:34 -04:00
include GitlabRoutingHelper
include PageLayoutHelper
2018-04-08 00:35:30 -04:00
include SafeParamsHelper
2016-06-06 07:16:30 -04:00
include WorkhorseHelper
2017-02-06 11:07:37 -05:00
include EnforcesTwoFactorAuthentication
2017-06-21 10:59:13 -04:00
include WithPerformanceBar
2018-11-28 14:06:02 -05:00
include SessionlessAuthentication
2015-01-08 12:53:35 -05:00
2015-04-16 08:03:37 -04:00
before_action :authenticate_user!
2018-05-10 05:35:02 -04:00
before_action :enforce_terms! , if : :should_enforce_terms?
2015-11-11 23:25:31 -05:00
before_action :validate_user_service_ticket!
2015-04-16 08:03:37 -04:00
before_action :check_password_expiration
before_action :ldap_security_check
2016-04-14 13:56:30 -04:00
before_action :sentry_context
2015-04-16 08:03:37 -04:00
before_action :default_headers
2018-08-07 17:28:57 -04:00
before_action :add_gon_variables , unless : [ :peek_request? , :json_request? ]
2015-04-16 08:03:37 -04:00
before_action :configure_permitted_parameters , if : :devise_controller?
before_action :require_email , unless : :devise_controller?
2018-09-07 10:32:28 -04:00
before_action :set_usage_stats_consent_flag
2018-11-24 07:39:16 -05:00
before_action :check_impersonation_availability
2012-06-24 03:01:42 -04:00
2017-05-03 00:36:36 -04:00
around_action :set_locale
2018-08-07 17:28:57 -04:00
after_action :set_page_title_header , if : :json_request?
2018-08-10 15:15:06 -04:00
after_action :limit_unauthenticated_session_times
2017-08-23 07:23:41 -04:00
2018-06-21 06:44:31 -04:00
protect_from_forgery with : :exception , prepend : true
2012-06-24 03:01:42 -04:00
2018-02-02 13:39:55 -05:00
helper_method :can?
2018-07-05 08:46:39 -04:00
helper_method :import_sources_enabled? , :github_import_enabled? ,
:gitea_import_enabled? , :github_import_configured? ,
:gitlab_import_enabled? , :gitlab_import_configured? ,
:bitbucket_import_enabled? , :bitbucket_import_configured? ,
2018-07-12 08:21:37 -04:00
:bitbucket_server_import_enabled? ,
2018-07-05 08:46:39 -04:00
:google_code_import_enabled? , :fogbugz_import_enabled? ,
:git_import_enabled? , :gitlab_project_import_enabled? ,
:manifest_import_enabled?
2011-10-08 17:36:38 -04:00
2018-10-18 07:28:12 -04:00
DEFAULT_GITLAB_CACHE_CONTROL = " #{ ActionDispatch :: Http :: Cache :: Response :: DEFAULT_CACHE_CONTROL } , no-store " . freeze
2012-05-31 16:36:52 -04:00
rescue_from Encoding :: CompatibilityError do | exception |
2012-11-06 15:15:25 -05:00
log_exception ( exception )
2012-09-26 16:24:52 -04:00
render " errors/encoding " , layout : " errors " , status : 500
2012-05-31 16:36:52 -04:00
end
2012-02-22 00:14:54 -05:00
rescue_from ActiveRecord :: RecordNotFound do | exception |
2012-11-06 15:15:25 -05:00
log_exception ( exception )
2015-10-09 13:07:29 -04:00
render_404
2011-10-09 17:15:28 -04:00
end
2017-06-20 09:53:05 -04:00
rescue_from ( ActionController :: UnknownFormat ) do
render_404
end
2016-06-17 12:59:33 -04:00
rescue_from Gitlab :: Access :: AccessDeniedError do | exception |
render_403
end
2017-02-20 09:09:05 -05:00
rescue_from Gitlab :: Auth :: TooManyIps do | e |
2017-02-21 16:17:23 -05:00
head :forbidden , retry_after : Gitlab :: Auth :: UniqueIpsLimiter . config . unique_ips_limit_time_window
2017-02-20 09:09:05 -05:00
end
2018-10-09 01:59:42 -04:00
rescue_from GRPC :: Unavailable , Gitlab :: Git :: CommandError do | exception |
2017-05-17 12:17:15 -04:00
log_exception ( exception )
headers [ 'Retry-After' ] = exception . retry_after if exception . respond_to? ( :retry_after )
render_503
end
2015-10-20 03:28:28 -04:00
def redirect_back_or_default ( default : root_path , options : { } )
2018-12-17 12:36:09 -05:00
redirect_back ( fallback_location : default , ** options )
2015-10-20 03:28:28 -04:00
end
2016-10-14 17:11:19 -04:00
def not_found
render_404
end
2016-11-14 09:55:31 -05:00
def route_not_found
if current_user
not_found
else
2017-05-01 16:46:30 -04:00
authenticate_user!
2016-11-14 09:55:31 -05:00
end
end
2018-07-18 14:18:14 -04:00
# By default, all sessions are given the same expiration time configured in
# the session store (e.g. 1 week). However, unauthenticated users can
# generate a lot of sessions, primarily for CSRF verification. It makes
# sense to reduce the TTL for unauthenticated to something much lower than
# the default (e.g. 1 hour) to limit Redis memory. In addition, Rails
# creates a new session after login, so the short TTL doesn't even need to
# be extended.
def limit_unauthenticated_session_times
return if current_user
# Rack sets this header, but not all tests may have it: https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L251-L259
return unless request . env [ 'rack.session.options' ]
# This works because Rack uses these options every time a request is handled:
# https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L342
request . env [ 'rack.session.options' ] [ :expire_after ] = Settings . gitlab [ 'unauthenticated_session_expire_delay' ]
end
2018-10-01 13:43:40 -04:00
def render ( * args )
super . tap do
# Set a header for custom error pages to prevent them from being intercepted by gitlab-workhorse
if response . content_type == 'text/html' && ( 400 .. 599 ) . cover? ( response . status )
response . headers [ 'X-GitLab-Custom-Error' ] = '1'
end
end
end
2011-10-26 09:46:25 -04:00
protected
2011-10-08 17:36:38 -04:00
2017-07-28 01:48:03 -04:00
def append_info_to_payload ( payload )
super
2018-08-01 08:23:06 -04:00
2018-09-05 17:40:36 -04:00
payload [ :ua ] = request . env [ " HTTP_USER_AGENT " ]
2017-07-28 01:48:03 -04:00
payload [ :remote_ip ] = request . remote_ip
2018-12-05 15:54:40 -05:00
payload [ Gitlab :: CorrelationId :: LOG_KEY ] = Gitlab :: CorrelationId . current_id
2017-07-28 01:48:03 -04:00
2017-10-03 02:28:22 -04:00
logged_user = auth_user
if logged_user . present?
payload [ :user_id ] = logged_user . try ( :id )
payload [ :username ] = logged_user . try ( :username )
2017-07-28 01:48:03 -04:00
end
2018-06-06 03:47:53 -04:00
if response . status == 422 && response . body . present? && response . content_type == 'application/json' . freeze
payload [ :response ] = response . body
end
2019-02-10 19:43:44 -05:00
payload [ :queue_duration ] = request . env [ :: Gitlab :: Middleware :: RailsQueueDuration :: GITLAB_RAILS_QUEUE_DURATION_KEY ]
2017-07-28 01:48:03 -04:00
end
2018-08-01 08:23:06 -04:00
##
2017-10-03 02:28:22 -04:00
# Controllers such as GitHttpController may use alternative methods
2018-08-01 08:23:06 -04:00
# (e.g. tokens) to authenticate the user, whereas Devise sets current_user.
#
2017-10-03 02:28:22 -04:00
def auth_user
2018-08-01 10:57:59 -04:00
if user_signed_in?
2018-08-01 08:23:06 -04:00
current_user
else
try ( :authenticated_user )
end
2017-10-03 02:28:22 -04:00
end
2012-11-06 15:15:25 -05:00
def log_exception ( exception )
2018-12-05 15:54:40 -05:00
Gitlab :: Sentry . track_acceptable_exception ( exception )
2017-07-04 17:02:01 -04:00
2018-12-15 04:06:56 -05:00
backtrace_cleaner = request . env [ " action_dispatch.backtrace_cleaner " ]
2018-04-28 05:01:31 -04:00
application_trace = ActionDispatch :: ExceptionWrapper . new ( backtrace_cleaner , exception ) . application_trace
2017-08-09 05:52:22 -04:00
application_trace . map! { | t | " #{ t } \n " }
2012-11-06 15:15:25 -05:00
logger . error " \n #{ exception . class . name } ( #{ exception . message } ): \n #{ application_trace . join } "
end
2014-09-25 18:07:40 -04:00
def after_sign_in_path_for ( resource )
2017-02-01 13:21:28 -05:00
stored_location_for ( :redirect ) || stored_location_for ( resource ) || root_path
2012-04-13 01:12:34 -04:00
end
2015-04-18 06:06:50 -04:00
def after_sign_out_path_for ( resource )
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . after_sign_out_path . presence || new_user_session_path
2015-04-18 06:06:50 -04:00
end
2017-02-28 16:08:07 -05:00
def can? ( object , action , subject = :global )
2016-08-08 14:55:13 -04:00
Ability . allowed? ( object , action , subject )
2011-10-08 17:36:38 -04:00
end
2018-11-12 16:40:42 -05:00
def access_denied! ( message = nil , status = nil )
2018-06-04 11:04:04 -04:00
# If we display a custom access denied message to the user, we don't want to
# hide existence of the resource, rather tell them they cannot access it using
# the provided message
2018-11-12 16:40:42 -05:00
status || = message . present? ? :forbidden : :not_found
2018-12-19 07:04:24 -05:00
template =
if status == :not_found
" errors/not_found "
else
" errors/access_denied "
end
2018-06-04 11:04:04 -04:00
2017-05-09 00:15:34 -04:00
respond_to do | format |
2018-06-04 11:04:04 -04:00
format . any { head status }
2017-12-11 09:21:06 -05:00
format . html do
2018-12-19 07:04:24 -05:00
render template ,
2017-12-11 09:21:06 -05:00
layout : " errors " ,
2018-06-04 11:04:04 -04:00
status : status ,
2017-12-11 09:21:06 -05:00
locals : { message : message }
end
2017-05-09 00:15:34 -04:00
end
2012-02-22 00:14:54 -05:00
end
def git_not_found!
2016-02-15 16:38:27 -05:00
render " errors/git_not_found.html " , layout : " errors " , status : 404
2011-10-08 17:36:38 -04:00
end
2014-02-07 11:59:55 -05:00
def render_403
2018-05-31 17:28:19 -04:00
respond_to do | format |
format . any { head :forbidden }
format . html { render " errors/access_denied " , layout : " errors " , status : 403 }
end
2011-10-14 11:08:25 -04:00
end
2011-10-15 11:51:58 -04:00
2014-02-07 11:59:55 -05:00
def render_404
2016-09-01 08:59:10 -04:00
respond_to do | format |
2018-05-31 17:28:19 -04:00
format . html { render " errors/not_found " , layout : " errors " , status : 404 }
2018-01-24 01:02:33 -05:00
# Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file
format . js { render json : '' , status : :not_found , content_type : 'application/json' }
2016-09-01 11:20:29 -04:00
format . any { head :not_found }
2016-09-01 08:59:10 -04:00
end
2012-12-04 22:14:05 -05:00
end
2017-04-28 05:38:32 -04:00
def respond_422
head :unprocessable_entity
end
2017-05-17 12:17:15 -04:00
def render_503
respond_to do | format |
format . html do
render (
file : Rails . root . join ( " public " , " 503 " ) ,
layout : false ,
status : :service_unavailable
)
end
format . any { head :service_unavailable }
end
end
2011-11-20 15:32:12 -05:00
def no_cache_headers
response . headers [ " Cache-Control " ] = " no-cache, no-store, max-age=0, must-revalidate "
response . headers [ " Pragma " ] = " no-cache "
response . headers [ " Expires " ] = " Fri, 01 Jan 1990 00:00:00 GMT "
end
2012-01-27 18:49:14 -05:00
2013-02-02 13:32:13 -05:00
def default_headers
headers [ 'X-Frame-Options' ] = 'DENY'
headers [ 'X-XSS-Protection' ] = '1; mode=block'
2013-12-27 06:38:29 -05:00
headers [ 'X-UA-Compatible' ] = 'IE=edge'
2013-12-30 03:41:05 -05:00
headers [ 'X-Content-Type-Options' ] = 'nosniff'
2018-10-18 07:28:12 -04:00
if current_user
# Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
# concerns due to caching private data.
headers [ 'Cache-Control' ] = DEFAULT_GITLAB_CACHE_CONTROL
headers [ " Pragma " ] = " no-cache " # HTTP 1.0 compatibility
end
2013-02-02 13:32:13 -05:00
end
2013-02-19 07:37:43 -05:00
2015-11-11 23:25:31 -05:00
def validate_user_service_ticket!
return unless signed_in? && session [ :service_tickets ]
valid = session [ :service_tickets ] . all? do | provider , ticket |
2018-02-23 07:10:39 -05:00
Gitlab :: Auth :: OAuth :: Session . valid? ( provider , ticket )
2015-11-11 23:25:31 -05:00
end
unless valid
session [ :service_tickets ] = nil
sign_out current_user
redirect_to new_user_session_path
end
end
2013-06-13 12:53:04 -04:00
def check_password_expiration
2017-11-23 08:16:14 -05:00
return if session [ :impersonator_id ] || ! current_user & . allow_password_authentication?
2017-09-13 07:18:15 -04:00
password_expires_at = current_user & . password_expires_at
if password_expires_at && password_expires_at < Time . now
2017-02-21 17:31:14 -05:00
return redirect_to new_profile_password_path
2013-06-13 12:53:04 -04:00
end
end
2013-08-26 09:30:03 -04:00
2014-03-10 11:10:23 -04:00
def ldap_security_check
2014-03-11 04:24:07 -04:00
if current_user && current_user . requires_ldap_check?
2016-03-10 06:37:14 -05:00
return unless current_user . try_obtain_ldap_lease
2016-03-09 13:11:24 -05:00
2018-02-23 07:10:39 -05:00
unless Gitlab :: Auth :: LDAP :: Access . allowed? ( current_user )
2014-07-30 03:50:50 -04:00
sign_out current_user
flash [ :alert ] = " Access denied for your LDAP account. "
redirect_to new_user_session_path
2014-03-10 11:10:23 -04:00
end
end
end
2013-08-26 09:30:03 -04:00
def event_filter
2018-09-06 06:07:33 -04:00
@event_filter || =
EventFilter . new ( params [ :event_filter ] . presence || cookies [ :event_filter ] ) . tap do | new_event_filter |
cookies [ :event_filter ] = new_event_filter . filter
end
2013-08-26 09:30:03 -04:00
end
2013-11-28 04:38:20 -05:00
# JSON for infinite scroll via Pager object
2016-10-21 04:22:43 -04:00
def pager_json ( partial , count , locals = { } )
2013-11-28 04:38:20 -05:00
html = render_to_string (
partial ,
2016-10-21 04:22:43 -04:00
locals : locals ,
2013-11-28 04:38:20 -05:00
layout : false ,
formats : [ :html ]
)
render json : {
html : html ,
count : count
}
end
2013-11-29 08:05:32 -05:00
2016-02-03 18:21:14 -05:00
def view_to_html_string ( partial , locals = { } )
2013-11-29 08:05:32 -05:00
render_to_string (
2016-02-03 18:21:14 -05:00
partial ,
2016-02-03 16:33:01 -05:00
locals : locals ,
2013-11-29 08:05:32 -05:00
layout : false ,
formats : [ :html ]
)
end
2013-12-10 06:35:10 -05:00
def configure_permitted_parameters
2016-05-19 14:52:08 -04:00
devise_parameter_sanitizer . permit ( :sign_in , keys : [ :username , :email , :password , :login , :remember_me , :otp_attempt ] )
2013-12-10 06:35:10 -05:00
end
2014-02-26 07:06:31 -05:00
def hexdigest ( string )
Digest :: SHA1 . hexdigest string
end
2014-04-07 07:09:29 -04:00
def require_email
2016-11-17 23:21:02 -05:00
if current_user && current_user . temp_oauth_email? && session [ :impersonator_id ] . nil?
2017-02-21 17:31:14 -05:00
return redirect_to profile_path , notice : 'Please complete your profile with email address'
2014-04-07 07:09:29 -04:00
end
end
2014-12-23 11:49:39 -05:00
2018-04-27 10:50:33 -04:00
def enforce_terms!
return unless current_user
return if current_user . terms_accepted?
2018-06-13 10:05:55 -04:00
message = _ ( " Please accept the Terms of Service before continuing. " )
2018-04-27 10:50:33 -04:00
if sessionless_user?
2018-06-13 10:05:55 -04:00
access_denied! ( message )
2018-04-27 10:50:33 -04:00
else
# Redirect to the destination if the request is a get.
# Redirect to the source if it was a post, so the user can re-submit after
# accepting the terms.
redirect_path = if request . get?
request . fullpath
else
URI ( request . referer ) . path if request . referer
end
2018-06-13 10:05:55 -04:00
flash [ :notice ] = message
2018-04-27 10:50:33 -04:00
redirect_to terms_path ( redirect : redirect_path ) , status : :found
end
end
2015-08-14 09:56:33 -04:00
def import_sources_enabled?
2018-02-02 13:39:55 -05:00
! Gitlab :: CurrentSettings . import_sources . empty?
2015-08-14 09:56:33 -04:00
end
2018-06-25 16:06:10 -04:00
def bitbucket_server_import_enabled?
Gitlab :: CurrentSettings . import_sources . include? ( 'bitbucket_server' )
end
2015-02-17 16:52:32 -05:00
def github_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'github' )
2015-08-14 09:56:33 -04:00
end
2016-12-15 11:31:14 -05:00
def gitea_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'gitea' )
2016-10-17 11:58:57 -04:00
end
2015-08-14 09:56:33 -04:00
def github_import_configured?
2018-02-23 07:10:39 -05:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :github )
2015-02-17 16:52:32 -05:00
end
def gitlab_import_enabled?
2018-02-02 13:39:55 -05:00
request . host != 'gitlab.com' && Gitlab :: CurrentSettings . import_sources . include? ( 'gitlab' )
2015-08-14 09:56:33 -04:00
end
def gitlab_import_configured?
2018-02-23 07:10:39 -05:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :gitlab )
2015-02-17 16:52:32 -05:00
end
def bitbucket_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'bitbucket' )
2015-08-14 09:56:33 -04:00
end
def bitbucket_import_configured?
2018-02-23 07:10:39 -05:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :bitbucket )
2015-02-17 16:52:32 -05:00
end
2015-08-14 09:56:33 -04:00
def google_code_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'google_code' )
2015-08-14 09:56:33 -04:00
end
2015-08-04 18:21:12 -04:00
def fogbugz_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'fogbugz' )
2015-08-04 18:21:12 -04:00
end
2015-08-14 09:56:33 -04:00
def git_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'git' )
2015-08-14 09:56:33 -04:00
end
2015-10-28 12:39:22 -04:00
2016-04-22 11:44:59 -04:00
def gitlab_project_import_enabled?
2018-02-02 13:39:55 -05:00
Gitlab :: CurrentSettings . import_sources . include? ( 'gitlab_project' )
2016-04-22 11:44:59 -04:00
end
2018-07-05 08:46:39 -04:00
def manifest_import_enabled?
2018-12-18 07:15:51 -05:00
Group . supports_nested_objects? && Gitlab :: CurrentSettings . import_sources . include? ( 'manifest' )
2018-07-05 08:46:39 -04:00
end
2016-06-06 00:44:51 -04:00
# U2F (universal 2nd factor) devices need a unique identifier for the application
# to perform authentication.
# https://developers.yubico.com/U2F/App_ID.html
def u2f_app_id
request . base_url
end
2017-04-13 02:03:47 -04:00
2017-05-25 11:22:45 -04:00
def set_locale ( & block )
Gitlab :: I18n . with_user_locale ( current_user , & block )
2017-04-13 02:03:47 -04:00
end
2017-05-23 09:59:33 -04:00
2017-08-23 07:23:41 -04:00
def set_page_title_header
2017-09-26 12:05:19 -04:00
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
2017-10-20 12:44:29 -04:00
response . headers [ 'Page-Title' ] = URI . escape ( page_title ( 'GitLab' ) )
2017-08-23 07:23:41 -04:00
end
2018-04-27 10:50:33 -04:00
def peek_request?
request . path . start_with? ( '/-/peek' )
end
2018-05-10 05:35:02 -04:00
2018-08-07 17:28:57 -04:00
def json_request?
request . format . json?
end
2018-05-10 05:35:02 -04:00
def should_enforce_terms?
return false unless Gitlab :: CurrentSettings . current_application_settings . enforce_terms
! ( peek_request? || devise_controller? )
end
2018-09-07 10:32:28 -04:00
def set_usage_stats_consent_flag
return unless current_user
return if sessionless_user?
return if session . has_key? ( :ask_for_usage_stats_consent )
session [ :ask_for_usage_stats_consent ] = current_user . requires_usage_stats_consent?
if session [ :ask_for_usage_stats_consent ]
disable_usage_stats
end
end
def disable_usage_stats
application_setting_params = {
usage_ping_enabled : false ,
version_check_enabled : false ,
skip_usage_stats_user : true
}
settings = Gitlab :: CurrentSettings . current_application_settings
ApplicationSettings :: UpdateService
. new ( settings , current_user , application_setting_params )
. execute
end
2018-11-24 07:39:16 -05:00
def check_impersonation_availability
return unless session [ :impersonator_id ]
unless Gitlab . config . gitlab . impersonation_enabled
stop_impersonation
access_denied! _ ( 'Impersonation has been disabled' )
end
end
def stop_impersonation
impersonated_user = current_user
Gitlab :: AppLogger . info ( " User #{ impersonator . username } has stopped impersonating #{ impersonated_user . username } " )
warden . set_user ( impersonator , scope : :user )
session [ :impersonator_id ] = nil
impersonated_user
end
def impersonator
@impersonator || = User . find ( session [ :impersonator_id ] ) if session [ :impersonator_id ]
end
2018-12-05 15:54:40 -05:00
def sentry_context
Gitlab :: Sentry . context ( current_user )
end
2011-10-08 17:36:38 -04:00
end