diff options
Diffstat (limited to 'app/controllers/concerns')
14 files changed, 198 insertions, 69 deletions
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 9ff97f398f5..5c74d79951f 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -89,10 +89,7 @@ module AuthenticatesWithTwoFactor user.save! sign_in(user, message: :two_factor_authenticated, event: :authentication) else - user.increment_failed_attempts! - Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") - flash.now[:alert] = _('Invalid two-factor code.') - prompt_for_two_factor(user) + handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.')) end end @@ -101,7 +98,7 @@ module AuthenticatesWithTwoFactor if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge]) handle_two_factor_success(user) else - handle_two_factor_failure(user, 'U2F') + handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) end end @@ -109,7 +106,7 @@ module AuthenticatesWithTwoFactor if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute handle_two_factor_success(user) else - handle_two_factor_failure(user, 'WebAuthn') + handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.')) end end @@ -152,13 +149,19 @@ module AuthenticatesWithTwoFactor sign_in(user, message: :two_factor_authenticated, event: :authentication) end - def handle_two_factor_failure(user, method) + def handle_two_factor_failure(user, method, message) user.increment_failed_attempts! + log_failed_two_factor(user, method, request.remote_ip) + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}") - flash.now[:alert] = _('Authentication via %{method} device failed.') % { method: method } + flash.now[:alert] = message prompt_for_two_factor(user) end + def log_failed_two_factor(user, method, ip_address) + # overridden in EE + end + def handle_changed_user(user) clear_two_factor_attempt! @@ -173,3 +176,5 @@ module AuthenticatesWithTwoFactor Digest::SHA256.hexdigest(user.encrypted_password) != session[:user_password_hash] end end + +AuthenticatesWithTwoFactor.prepend_if_ee('EE::AuthenticatesWithTwoFactor') diff --git a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb new file mode 100644 index 00000000000..a8155f1e639 --- /dev/null +++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +module AuthenticatesWithTwoFactorForAdminMode + extend ActiveSupport::Concern + + included do + include AuthenticatesWithTwoFactor + end + + def admin_mode_prompt_for_two_factor(user) + return handle_locked_user(user) unless user.can?(:log_in) + + session[:otp_user_id] = user.id + push_frontend_feature_flag(:webauthn) + + if user.two_factor_webauthn_enabled? + setup_webauthn_authentication(user) + else + setup_u2f_authentication(user) + end + + render 'admin/sessions/two_factor', layout: 'application' + end + + def admin_mode_authenticate_with_two_factor + user = current_user + + return handle_locked_user(user) unless user.can?(:log_in) + + if user_params[:otp_attempt].present? && session[:otp_user_id] + admin_mode_authenticate_with_two_factor_via_otp(user) + elsif user_params[:device_response].present? && session[:otp_user_id] + if user.two_factor_webauthn_enabled? + admin_mode_authenticate_with_two_factor_via_webauthn(user) + else + admin_mode_authenticate_with_two_factor_via_u2f(user) + end + elsif user && user.valid_password?(user_params[:password]) + admin_mode_prompt_for_two_factor(user) + else + invalid_login_redirect + end + end + + def admin_mode_authenticate_with_two_factor_via_otp(user) + if valid_otp_attempt?(user) + # Remove any lingering user data from login + session.delete(:otp_user_id) + + user.save! unless Gitlab::Database.read_only? + + # The admin user has successfully passed 2fa, enable admin mode ignoring password + enable_admin_mode + else + admin_handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.')) + end + end + + def admin_mode_authenticate_with_two_factor_via_u2f(user) + if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge]) + admin_handle_two_factor_success + else + admin_handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) + end + end + + def admin_mode_authenticate_with_two_factor_via_webauthn(user) + if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute + admin_handle_two_factor_success + else + admin_handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.')) + end + end + + private + + def enable_admin_mode + if current_user_mode.enable_admin_mode!(skip_password_validation: true) + redirect_to redirect_path, notice: _('Admin mode enabled') + else + invalid_login_redirect + end + end + + def invalid_login_redirect + flash.now[:alert] = _('Invalid login or password') + render :new + end + + def admin_handle_two_factor_success + # Remove any lingering user data from login + session.delete(:otp_user_id) + session.delete(:challenge) + + # The admin user has successfully passed 2fa, enable admin mode ignoring password + enable_admin_mode + end + + def admin_handle_two_factor_failure(user, method, message) + user.increment_failed_attempts! + log_failed_two_factor(user, method, request.remote_ip) + + Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}") + flash.now[:alert] = message + admin_mode_prompt_for_two_factor(user) + end +end diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb index 9d40b9e8c88..b382e338a78 100644 --- a/app/controllers/concerns/boards_actions.rb +++ b/app/controllers/concerns/boards_actions.rb @@ -9,7 +9,7 @@ module BoardsActions before_action :boards, only: :index before_action :board, only: :show - before_action :push_wip_limits, only: [:index, :show] + before_action :push_licensed_features, only: [:index, :show] before_action do push_frontend_feature_flag(:not_issuable_queries, parent, default_enabled: true) end @@ -29,7 +29,7 @@ module BoardsActions private # Noop on FOSS - def push_wip_limits + def push_licensed_features end def boards diff --git a/app/controllers/concerns/controller_with_feature_category.rb b/app/controllers/concerns/controller_with_feature_category.rb index f8985cf0950..c1ff9ef2e69 100644 --- a/app/controllers/concerns/controller_with_feature_category.rb +++ b/app/controllers/concerns/controller_with_feature_category.rb @@ -5,35 +5,38 @@ module ControllerWithFeatureCategory include Gitlab::ClassAttributes class_methods do - def feature_category(category, config = {}) - validate_config!(config) + def feature_category(category, actions = []) + feature_category_configuration[category] ||= [] + feature_category_configuration[category] += actions.map(&:to_s) - category_config = Config.new(category, config[:only], config[:except], config[:if], config[:unless]) - # Add the config to the beginning. That way, the last defined one takes precedence. - feature_category_configuration.unshift(category_config) + validate_config!(feature_category_configuration) end def feature_category_for_action(action) - category_config = feature_category_configuration.find { |config| config.matches?(action) } + category_config = feature_category_configuration.find do |_, actions| + actions.empty? || actions.include?(action) + end - category_config&.category || superclass_feature_category_for_action(action) + category_config&.first || superclass_feature_category_for_action(action) end private def validate_config!(config) - invalid_keys = config.keys - [:only, :except, :if, :unless] - if invalid_keys.any? - raise ArgumentError, "unknown arguments: #{invalid_keys} " + empty = config.find { |_, actions| actions.empty? } + duplicate_actions = config.values.flatten.group_by(&:itself).select { |_, v| v.count > 1 }.keys + + if config.length > 1 && empty + raise ArgumentError, "#{empty.first} is defined for all actions, but other categories are set" end - if config.key?(:only) && config.key?(:except) - raise ArgumentError, "cannot configure both `only` and `except`" + if duplicate_actions.any? + raise ArgumentError, "Actions have multiple feature categories: #{duplicate_actions.join(', ')}" end end def feature_category_configuration - class_attributes[:feature_category_config] ||= [] + class_attributes[:feature_category_config] ||= {} end def superclass_feature_category_for_action(action) diff --git a/app/controllers/concerns/controller_with_feature_category/config.rb b/app/controllers/concerns/controller_with_feature_category/config.rb deleted file mode 100644 index 624691ee4f6..00000000000 --- a/app/controllers/concerns/controller_with_feature_category/config.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module ControllerWithFeatureCategory - class Config - attr_reader :category - - def initialize(category, only, except, if_proc, unless_proc) - @category = category.to_sym - @only, @except = only&.map(&:to_s), except&.map(&:to_s) - @if_proc, @unless_proc = if_proc, unless_proc - end - - def matches?(action) - included?(action) && !excluded?(action) && - if_proc?(action) && !unless_proc?(action) - end - - private - - attr_reader :only, :except, :if_proc, :unless_proc - - def if_proc?(action) - if_proc.nil? || if_proc.call(action) - end - - def unless_proc?(action) - unless_proc.present? && unless_proc.call(action) - end - - def included?(action) - only.nil? || only.include?(action) - end - - def excluded?(action) - except.present? && except.include?(action) - end - end -end diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb index 02082f81598..bf38e4ad117 100644 --- a/app/controllers/concerns/enforces_two_factor_authentication.rb +++ b/app/controllers/concerns/enforces_two_factor_authentication.rb @@ -11,7 +11,7 @@ module EnforcesTwoFactorAuthentication extend ActiveSupport::Concern included do - before_action :check_two_factor_requirement + before_action :check_two_factor_requirement, except: [:route_not_found] # to include this in controllers inheriting from `ActionController::Metal` # we need to add this block diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index 6060dc729af..39f63bbaaec 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -20,7 +20,7 @@ module IntegrationsActions respond_to do |format| format.html do if saved - PropagateIntegrationWorker.perform_async(integration.id, false) + PropagateIntegrationWorker.perform_async(integration.id) redirect_to scoped_edit_integration_path(integration), notice: success_message else render 'shared/integrations/edit' diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index e3ac117660b..7ed66027da3 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -59,6 +59,9 @@ module IssuableCollectionsAction end def finder_options - super.merge(non_archived: true) + super.merge( + non_archived: true, + issue_types: Issue::TYPES_FOR_LIST + ) end end diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 29138e7b014..6470c75dfbd 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -3,13 +3,25 @@ module MilestoneActions extend ActiveSupport::Concern + def issues + respond_to do |format| + format.html { redirect_to milestone_redirect_path } + format.json do + render json: tabs_json("shared/milestones/_issues_tab", { + issues: @milestone.sorted_issues(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables + show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name]) + }) + end + end + end + def merge_requests respond_to do |format| format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_merge_requests_tab", { merge_requests: @milestone.sorted_merge_requests(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables - show_project_name: true + show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name]) }) end end diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb index 95a6800f55c..370b8c72bfe 100644 --- a/app/controllers/concerns/multiple_boards_actions.rb +++ b/app/controllers/concerns/multiple_boards_actions.rb @@ -21,11 +21,13 @@ module MultipleBoardsActions end def create - board = Boards::CreateService.new(parent, current_user, board_params).execute + response = Boards::CreateService.new(parent, current_user, board_params).execute respond_to do |format| format.json do - if board.persisted? + board = response.payload + + if response.success? extra_json = { board_path: board_path(board) } render json: serialize_as_json(board).merge(extra_json) else diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb index fa5eef981d1..fdd22cc0da0 100644 --- a/app/controllers/concerns/redis_tracking.rb +++ b/app/controllers/concerns/redis_tracking.rb @@ -26,7 +26,6 @@ module RedisTracking def track_unique_redis_hll_event(event_name, feature, feature_default_enabled) return unless metric_feature_enabled?(feature, feature_default_enabled) - return unless Gitlab::CurrentSettings.usage_ping_enabled? return unless visitor_id Gitlab::UsageDataCounters::HLLRedisCounter.track_event(visitor_id, event_name) diff --git a/app/controllers/concerns/runner_setup_scripts.rb b/app/controllers/concerns/runner_setup_scripts.rb new file mode 100644 index 00000000000..c0e657a32d1 --- /dev/null +++ b/app/controllers/concerns/runner_setup_scripts.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module RunnerSetupScripts + extend ActiveSupport::Concern + + private + + def private_runner_setup_scripts(**kwargs) + instructions = Gitlab::Ci::RunnerInstructions.new(current_user: current_user, os: script_params[:os], arch: script_params[:arch], **kwargs) + output = { + install: instructions.install_script, + register: instructions.register_command + } + + if instructions.errors.any? + render json: { errors: instructions.errors }, status: :bad_request + else + render json: output + end + end + + def script_params + params.permit(:os, :arch) + end +end diff --git a/app/controllers/concerns/show_inherited_labels_checker.rb b/app/controllers/concerns/show_inherited_labels_checker.rb new file mode 100644 index 00000000000..acbea37a62e --- /dev/null +++ b/app/controllers/concerns/show_inherited_labels_checker.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module ShowInheritedLabelsChecker + extend ActiveSupport::Concern + + private + + def show_inherited_labels?(include_ancestor_groups) + Feature.enabled?(:show_inherited_labels, @project || @group) || include_ancestor_groups # rubocop:disable Gitlab/ModuleWithInstanceVariables + end +end diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 5a5b634da40..4d1684ec3a2 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -44,7 +44,7 @@ module WikiActions wiki.list_pages(sort: params[:sort], direction: params[:direction]) ).page(params[:page]) - @wiki_entries = WikiPage.group_by_directory(@wiki_pages) + @wiki_entries = WikiDirectory.group_pages(@wiki_pages) render 'shared/wikis/pages' end |