Files
codeocean/app/controllers/application_controller.rb
Sebastian Serth 99bd46af1a Align project files with CodeHarbor
Since both projects are developed together and by the same team, we also want to have the same code structure and utility methods available in both projects. Therefore, this commit changes many files, but without a functional change.
2023-10-11 00:18:33 +02:00

173 lines
5.2 KiB
Ruby

# frozen_string_literal: true
class ApplicationController < ActionController::Base
include ApplicationHelper
include Pundit::Authorization
MEMBER_ACTIONS = %i[destroy edit show update].freeze
RENDER_HOST = CodeOcean::Config.new(:code_ocean).read[:render_host]
LEGAL_SETTINGS = CodeOcean::Config.new(:code_ocean).read[:legal] || {}
MONITORING_USER_AGENT = /updown\.io/
before_action :deny_access_from_render_host
after_action :verify_authorized, except: %i[welcome]
around_action :mnemosyne_trace
around_action :switch_locale
before_action :set_sentry_context, :load_embed_options, :set_document_policy
protect_from_forgery(with: :exception, prepend: true)
rescue_from Pundit::NotAuthorizedError, with: :render_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::InvalidAuthenticityToken, with: :render_csrf_error
add_flash_types :danger, :warning, :info, :success
def current_user
@current_user ||= find_or_login_current_user&.store_current_study_group_id(session[:study_group_id])
end
def current_contributor
@current_contributor ||= if session[:pg_id]
current_user.programming_groups.find(session[:pg_id])
else
current_user
end
end
helper_method :current_contributor
def welcome
# Show root page
redirect_to ping_index_path if MONITORING_USER_AGENT.match?(request.user_agent)
end
private
def require_user!
raise Pundit::NotAuthorizedError unless current_user
end
def deny_access_from_render_host
raise Pundit::NotAuthorizedError if RENDER_HOST.present? && request.host == RENDER_HOST
end
def load_embed_options
@embed_options = if session[:embed_options].present? && session[:embed_options].is_a?(Hash)
session[:embed_options].symbolize_keys
else
{}
end
Sentry.set_extras(@embed_options)
@embed_options
end
def find_or_login_current_user
login_from_authentication_token ||
login_from_lti_session ||
login_from_session ||
login_from_other_sources ||
nil
end
def login_from_lti_session
return unless session[:external_user_id]
ExternalUser.find_by(id: session[:external_user_id])
end
def login_from_authentication_token
return unless params[:token]
token = AuthenticationToken.find_by(shared_secret: params[:token])
return unless token
if token.expire_at.future?
token.update(expire_at: Time.zone.now)
session[:study_group_id] = token.study_group_id
# Sorcery Login only works for InternalUsers
return auto_login(token.user) if token.user.is_a? InternalUser
# All external users are logged in "manually"
session[:external_user_id] = token.user.id
token.user
end
end
def set_sentry_context
return if current_user.blank?
Sentry.set_user(
id: current_user.id,
type: current_user.class.name,
consumer: current_user.consumer&.name
)
end
def set_document_policy
# Instruct browsers to capture profiling data
response.set_header('Document-Policy', 'js-profiling')
end
def render_csrf_error
render_error t('sessions.expired'), :unprocessable_entity
end
def render_not_authorized
render_error t('application.not_authorized'), :unauthorized
end
def render_not_found
if current_user&.admin?
render_error t('application.not_found'), :not_found
else
render_not_authorized
end
end
def render_error(message, status)
set_sentry_context
respond_to do |format|
format.any do
# Prevent redirect loop
if request.url == request.referer
redirect_to :root, alert: message
# Redirect to main domain if the request originated from our render_host
elsif request.path == '/' && request.host == RENDER_HOST
redirect_to Rails.application.config.action_mailer.default_url_options, allow_other_host: true
else
redirect_back fallback_location: :root, allow_other_host: false, alert: message
end
end
format.json { render json: {error: message}, status: }
end
end
def mnemosyne_trace
yield
ensure
if ::Mnemosyne::Instrumenter.current_trace.present?
::Mnemosyne::Instrumenter.current_trace.meta['session_id'] = session[:session_id]
::Mnemosyne::Instrumenter.current_trace.meta['csrf_token'] = session[:_csrf_token]
::Mnemosyne::Instrumenter.current_trace.meta['external_user_id'] = session[:external_user_id]
end
end
def switch_locale(&)
session[:locale] = sanitize_locale(params[:custom_locale] || params[:locale] || session[:locale])
locale = session[:locale] || http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
Sentry.set_extras(locale:)
I18n.with_locale(locale, &)
end
# Sanitize given locale.
#
# Return `nil` if the locale is blank or not available.
#
def sanitize_locale(locale)
return if locale.blank?
locale = locale.downcase.to_sym
return unless I18n.available_locales.include?(locale)
locale
end
end