2013-12-06 15:05:34 +00:00
require 'gon'
2015-08-04 22:21:12 +00:00
require 'fogbugz'
2013-12-06 15:05:34 +00:00
2013-08-21 09:33:12 +00:00
class ApplicationController < ActionController :: Base
2016-04-14 12:28:46 +00:00
include Gitlab :: GonHelper
2015-09-09 12:37:34 +00:00
include GitlabRoutingHelper
include PageLayoutHelper
2018-04-08 04:35:30 +00:00
include SafeParamsHelper
2016-08-19 00:06:33 +00:00
include SentryHelper
2016-06-06 11:16:30 +00:00
include WorkhorseHelper
2017-02-06 16:07:37 +00:00
include EnforcesTwoFactorAuthentication
2017-06-21 14:59:13 +00:00
include WithPerformanceBar
2015-01-08 17:53:35 +00:00
2017-09-15 17:31:32 +00:00
before_action :authenticate_sessionless_user!
2015-04-16 12:03:37 +00:00
before_action :authenticate_user!
2018-05-10 09:35:02 +00:00
before_action :enforce_terms! , if : :should_enforce_terms?
2015-11-12 04:25:31 +00:00
before_action :validate_user_service_ticket!
2015-04-16 12:03:37 +00:00
before_action :check_password_expiration
before_action :ldap_security_check
2016-04-14 17:56:30 +00:00
before_action :sentry_context
2015-04-16 12:03:37 +00:00
before_action :default_headers
2018-08-07 21:28:57 +00:00
before_action :add_gon_variables , unless : [ :peek_request? , :json_request? ]
2015-04-16 12:03:37 +00:00
before_action :configure_permitted_parameters , if : :devise_controller?
before_action :require_email , unless : :devise_controller?
2012-06-24 07:01:42 +00:00
2017-05-03 04:36:36 +00:00
around_action :set_locale
2018-08-07 21:28:57 +00:00
after_action :set_page_title_header , if : :json_request?
2018-08-10 19:15:06 +00:00
after_action :limit_unauthenticated_session_times
2017-08-23 11:23:41 +00:00
2018-06-21 10:44:31 +00:00
protect_from_forgery with : :exception , prepend : true
2012-06-24 07:01:42 +00:00
2018-02-02 18:39:55 +00:00
helper_method :can?
2018-07-05 12:46:39 +00: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 12:21:37 +00:00
:bitbucket_server_import_enabled? ,
2018-07-05 12:46:39 +00:00
:google_code_import_enabled? , :fogbugz_import_enabled? ,
:git_import_enabled? , :gitlab_project_import_enabled? ,
:manifest_import_enabled?
2011-10-08 21:36:38 +00:00
2012-05-31 20:36:52 +00:00
rescue_from Encoding :: CompatibilityError do | exception |
2012-11-06 20:15:25 +00:00
log_exception ( exception )
2012-09-26 20:24:52 +00:00
render " errors/encoding " , layout : " errors " , status : 500
2012-05-31 20:36:52 +00:00
end
2012-02-22 05:14:54 +00:00
rescue_from ActiveRecord :: RecordNotFound do | exception |
2012-11-06 20:15:25 +00:00
log_exception ( exception )
2015-10-09 17:07:29 +00:00
render_404
2011-10-09 21:15:28 +00:00
end
2017-06-20 13:53:05 +00:00
rescue_from ( ActionController :: UnknownFormat ) do
render_404
end
2016-06-17 16:59:33 +00:00
rescue_from Gitlab :: Access :: AccessDeniedError do | exception |
render_403
end
2017-02-20 14:09:05 +00:00
rescue_from Gitlab :: Auth :: TooManyIps do | e |
2017-02-21 21:17:23 +00:00
head :forbidden , retry_after : Gitlab :: Auth :: UniqueIpsLimiter . config . unique_ips_limit_time_window
2017-02-20 14:09:05 +00:00
end
2017-08-07 12:00:09 +00:00
rescue_from Gitlab :: Git :: Storage :: Inaccessible , GRPC :: Unavailable , Gitlab :: Git :: CommandError do | exception |
2017-05-17 16:17:15 +00:00
Raven . capture_exception ( exception ) if sentry_enabled?
log_exception ( exception )
headers [ 'Retry-After' ] = exception . retry_after if exception . respond_to? ( :retry_after )
render_503
end
2015-10-20 07:28:28 +00:00
def redirect_back_or_default ( default : root_path , options : { } )
redirect_to request . referer . present? ? :back : default , options
end
2016-10-14 21:11:19 +00:00
def not_found
render_404
end
2016-11-14 14:55:31 +00:00
def route_not_found
if current_user
not_found
else
2017-05-01 20:46:30 +00:00
authenticate_user!
2016-11-14 14:55:31 +00:00
end
end
2018-07-18 18:18:14 +00: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
2011-10-26 13:46:25 +00:00
protected
2011-10-08 21:36:38 +00:00
2017-07-28 05:48:03 +00:00
def append_info_to_payload ( payload )
super
2018-08-01 12:23:06 +00:00
2018-09-05 21:40:36 +00:00
payload [ :ua ] = request . env [ " HTTP_USER_AGENT " ]
2017-07-28 05:48:03 +00:00
payload [ :remote_ip ] = request . remote_ip
2017-10-03 06:28:22 +00: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 05:48:03 +00:00
end
2018-06-06 07:47:53 +00:00
if response . status == 422 && response . body . present? && response . content_type == 'application/json' . freeze
payload [ :response ] = response . body
end
2017-07-28 05:48:03 +00:00
end
2018-08-01 12:23:06 +00:00
##
2017-10-03 06:28:22 +00:00
# Controllers such as GitHttpController may use alternative methods
2018-08-01 12:23:06 +00:00
# (e.g. tokens) to authenticate the user, whereas Devise sets current_user.
#
2017-10-03 06:28:22 +00:00
def auth_user
2018-08-01 14:57:59 +00:00
if user_signed_in?
2018-08-01 12:23:06 +00:00
current_user
else
try ( :authenticated_user )
end
2017-10-03 06:28:22 +00:00
end
2017-11-08 18:41:07 +00:00
# This filter handles personal access tokens, and atom requests with rss tokens
2017-09-15 17:31:32 +00:00
def authenticate_sessionless_user!
2017-10-14 00:05:18 +00:00
user = Gitlab :: Auth :: RequestAuthenticator . new ( request ) . find_sessionless_user
2017-09-15 17:31:32 +00:00
sessionless_sign_in ( user ) if user
2016-04-19 10:52:15 +00:00
end
2012-11-06 20:15:25 +00:00
def log_exception ( exception )
2017-07-04 21:02:01 +00:00
Raven . capture_exception ( exception ) if sentry_enabled?
2018-04-28 09:01:31 +00:00
backtrace_cleaner = Gitlab . rails5? ? env [ " action_dispatch.backtrace_cleaner " ] : env
application_trace = ActionDispatch :: ExceptionWrapper . new ( backtrace_cleaner , exception ) . application_trace
2017-08-09 09:52:22 +00:00
application_trace . map! { | t | " #{ t } \n " }
2012-11-06 20:15:25 +00:00
logger . error " \n #{ exception . class . name } ( #{ exception . message } ): \n #{ application_trace . join } "
end
2014-09-25 22:07:40 +00:00
def after_sign_in_path_for ( resource )
2017-02-01 18:21:28 +00:00
stored_location_for ( :redirect ) || stored_location_for ( resource ) || root_path
2012-04-13 05:12:34 +00:00
end
2015-04-18 10:06:50 +00:00
def after_sign_out_path_for ( resource )
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . after_sign_out_path . presence || new_user_session_path
2015-04-18 10:06:50 +00:00
end
2017-02-28 21:08:07 +00:00
def can? ( object , action , subject = :global )
2016-08-08 18:55:13 +00:00
Ability . allowed? ( object , action , subject )
2011-10-08 21:36:38 +00:00
end
2017-12-11 14:21:06 +00:00
def access_denied! ( message = nil )
2018-06-04 15:04:04 +00: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
status = message . present? ? :forbidden : :not_found
2017-05-09 04:15:34 +00:00
respond_to do | format |
2018-06-04 15:04:04 +00:00
format . any { head status }
2017-12-11 14:21:06 +00:00
format . html do
render " errors/access_denied " ,
layout : " errors " ,
2018-06-04 15:04:04 +00:00
status : status ,
2017-12-11 14:21:06 +00:00
locals : { message : message }
end
2017-05-09 04:15:34 +00:00
end
2012-02-22 05:14:54 +00:00
end
def git_not_found!
2016-02-15 21:38:27 +00:00
render " errors/git_not_found.html " , layout : " errors " , status : 404
2011-10-08 21:36:38 +00:00
end
2014-02-07 16:59:55 +00:00
def render_403
2018-05-31 21:28:19 +00:00
respond_to do | format |
format . any { head :forbidden }
format . html { render " errors/access_denied " , layout : " errors " , status : 403 }
end
2011-10-14 15:08:25 +00:00
end
2011-10-15 15:51:58 +00:00
2014-02-07 16:59:55 +00:00
def render_404
2016-09-01 12:59:10 +00:00
respond_to do | format |
2018-05-31 21:28:19 +00:00
format . html { render " errors/not_found " , layout : " errors " , status : 404 }
2018-01-24 06:02:33 +00: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 15:20:29 +00:00
format . any { head :not_found }
2016-09-01 12:59:10 +00:00
end
2012-12-05 03:14:05 +00:00
end
2017-04-28 09:38:32 +00:00
def respond_422
head :unprocessable_entity
end
2017-05-17 16:17:15 +00: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 20:32:12 +00: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 23:49:14 +00:00
2013-02-02 18:32:13 +00:00
def default_headers
headers [ 'X-Frame-Options' ] = 'DENY'
headers [ 'X-XSS-Protection' ] = '1; mode=block'
2013-12-27 11:38:29 +00:00
headers [ 'X-UA-Compatible' ] = 'IE=edge'
2013-12-30 08:41:05 +00:00
headers [ 'X-Content-Type-Options' ] = 'nosniff'
2013-02-02 18:32:13 +00:00
end
2013-02-19 12:37:43 +00:00
2015-11-12 04:25:31 +00:00
def validate_user_service_ticket!
return unless signed_in? && session [ :service_tickets ]
valid = session [ :service_tickets ] . all? do | provider , ticket |
2018-02-23 12:10:39 +00:00
Gitlab :: Auth :: OAuth :: Session . valid? ( provider , ticket )
2015-11-12 04:25:31 +00:00
end
unless valid
session [ :service_tickets ] = nil
sign_out current_user
redirect_to new_user_session_path
end
end
2013-06-13 16:53:04 +00:00
def check_password_expiration
2017-11-23 13:16:14 +00:00
return if session [ :impersonator_id ] || ! current_user & . allow_password_authentication?
2017-09-13 11:18:15 +00:00
password_expires_at = current_user & . password_expires_at
if password_expires_at && password_expires_at < Time . now
2017-02-21 22:31:14 +00:00
return redirect_to new_profile_password_path
2013-06-13 16:53:04 +00:00
end
end
2013-08-26 13:30:03 +00:00
2014-03-10 15:10:23 +00:00
def ldap_security_check
2014-03-11 08:24:07 +00:00
if current_user && current_user . requires_ldap_check?
2016-03-10 11:37:14 +00:00
return unless current_user . try_obtain_ldap_lease
2016-03-09 18:11:24 +00:00
2018-02-23 12:10:39 +00:00
unless Gitlab :: Auth :: LDAP :: Access . allowed? ( current_user )
2014-07-30 07:50:50 +00:00
sign_out current_user
flash [ :alert ] = " Access denied for your LDAP account. "
redirect_to new_user_session_path
2014-03-10 15:10:23 +00:00
end
end
end
2013-08-26 13:30:03 +00:00
def event_filter
2016-08-29 17:09:33 +00:00
# Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
filters = cookies [ 'event_filter' ] . split ( ',' ) [ 0 ] if cookies [ 'event_filter' ] . present?
2013-08-26 13:30:03 +00:00
@event_filter || = EventFilter . new ( filters )
end
2013-11-28 09:38:20 +00:00
# JSON for infinite scroll via Pager object
2016-10-21 08:22:43 +00:00
def pager_json ( partial , count , locals = { } )
2013-11-28 09:38:20 +00:00
html = render_to_string (
partial ,
2016-10-21 08:22:43 +00:00
locals : locals ,
2013-11-28 09:38:20 +00:00
layout : false ,
formats : [ :html ]
)
render json : {
html : html ,
count : count
}
end
2013-11-29 13:05:32 +00:00
2016-02-03 23:21:14 +00:00
def view_to_html_string ( partial , locals = { } )
2013-11-29 13:05:32 +00:00
render_to_string (
2016-02-03 23:21:14 +00:00
partial ,
2016-02-03 21:33:01 +00:00
locals : locals ,
2013-11-29 13:05:32 +00:00
layout : false ,
formats : [ :html ]
)
end
2013-12-10 11:35:10 +00:00
def configure_permitted_parameters
2016-05-19 18:52:08 +00:00
devise_parameter_sanitizer . permit ( :sign_in , keys : [ :username , :email , :password , :login , :remember_me , :otp_attempt ] )
2013-12-10 11:35:10 +00:00
end
2014-02-26 12:06:31 +00:00
def hexdigest ( string )
Digest :: SHA1 . hexdigest string
end
2014-04-07 11:09:29 +00:00
def require_email
2016-11-18 04:21:02 +00:00
if current_user && current_user . temp_oauth_email? && session [ :impersonator_id ] . nil?
2017-02-21 22:31:14 +00:00
return redirect_to profile_path , notice : 'Please complete your profile with email address'
2014-04-07 11:09:29 +00:00
end
end
2014-12-23 16:49:39 +00:00
2018-04-27 14:50:33 +00:00
def enforce_terms!
return unless current_user
return if current_user . terms_accepted?
2018-06-13 14:05:55 +00:00
message = _ ( " Please accept the Terms of Service before continuing. " )
2018-04-27 14:50:33 +00:00
if sessionless_user?
2018-06-13 14:05:55 +00:00
access_denied! ( message )
2018-04-27 14:50:33 +00: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 14:05:55 +00:00
flash [ :notice ] = message
2018-04-27 14:50:33 +00:00
redirect_to terms_path ( redirect : redirect_path ) , status : :found
end
end
2015-08-14 13:56:33 +00:00
def import_sources_enabled?
2018-02-02 18:39:55 +00:00
! Gitlab :: CurrentSettings . import_sources . empty?
2015-08-14 13:56:33 +00:00
end
2018-06-25 20:06:10 +00:00
def bitbucket_server_import_enabled?
Gitlab :: CurrentSettings . import_sources . include? ( 'bitbucket_server' )
end
2015-02-17 21:52:32 +00:00
def github_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'github' )
2015-08-14 13:56:33 +00:00
end
2016-12-15 16:31:14 +00:00
def gitea_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'gitea' )
2016-10-17 15:58:57 +00:00
end
2015-08-14 13:56:33 +00:00
def github_import_configured?
2018-02-23 12:10:39 +00:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :github )
2015-02-17 21:52:32 +00:00
end
def gitlab_import_enabled?
2018-02-02 18:39:55 +00:00
request . host != 'gitlab.com' && Gitlab :: CurrentSettings . import_sources . include? ( 'gitlab' )
2015-08-14 13:56:33 +00:00
end
def gitlab_import_configured?
2018-02-23 12:10:39 +00:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :gitlab )
2015-02-17 21:52:32 +00:00
end
def bitbucket_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'bitbucket' )
2015-08-14 13:56:33 +00:00
end
def bitbucket_import_configured?
2018-02-23 12:10:39 +00:00
Gitlab :: Auth :: OAuth :: Provider . enabled? ( :bitbucket )
2015-02-17 21:52:32 +00:00
end
2015-08-14 13:56:33 +00:00
def google_code_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'google_code' )
2015-08-14 13:56:33 +00:00
end
2015-08-04 22:21:12 +00:00
def fogbugz_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'fogbugz' )
2015-08-04 22:21:12 +00:00
end
2015-08-14 13:56:33 +00:00
def git_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'git' )
2015-08-14 13:56:33 +00:00
end
2015-10-28 16:39:22 +00:00
2016-04-22 15:44:59 +00:00
def gitlab_project_import_enabled?
2018-02-02 18:39:55 +00:00
Gitlab :: CurrentSettings . import_sources . include? ( 'gitlab_project' )
2016-04-22 15:44:59 +00:00
end
2018-07-05 12:46:39 +00:00
def manifest_import_enabled?
2018-07-09 11:59:07 +00:00
Group . supports_nested_groups? && Gitlab :: CurrentSettings . import_sources . include? ( 'manifest' )
2018-07-05 12:46:39 +00:00
end
2016-06-06 04:44:51 +00: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 06:03:47 +00:00
2017-05-25 15:22:45 +00:00
def set_locale ( & block )
Gitlab :: I18n . with_user_locale ( current_user , & block )
2017-04-13 06:03:47 +00:00
end
2017-05-23 13:59:33 +00:00
def sessionless_sign_in ( user )
if user && can? ( user , :log_in )
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
2018-07-27 10:56:34 +00:00
sign_in ( user , store : false , message : :sessionless_sign_in )
2017-05-23 13:59:33 +00:00
end
end
2017-08-23 11:23:41 +00:00
def set_page_title_header
2017-09-26 16:05:19 +00:00
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
2017-10-20 16:44:29 +00:00
response . headers [ 'Page-Title' ] = URI . escape ( page_title ( 'GitLab' ) )
2017-08-23 11:23:41 +00:00
end
2018-04-27 14:50:33 +00:00
def sessionless_user?
current_user && ! session . keys . include? ( 'warden.user.user.key' )
end
def peek_request?
request . path . start_with? ( '/-/peek' )
end
2018-05-10 09:35:02 +00:00
2018-08-07 21:28:57 +00:00
def json_request?
request . format . json?
end
2018-05-10 09:35:02 +00:00
def should_enforce_terms?
return false unless Gitlab :: CurrentSettings . current_application_settings . enforce_terms
! ( peek_request? || devise_controller? )
end
2011-10-08 21:36:38 +00:00
end