diff options
Diffstat (limited to 'app/controllers/concerns')
14 files changed, 130 insertions, 61 deletions
diff --git a/app/controllers/concerns/floc_opt_out.rb b/app/controllers/concerns/floc_opt_out.rb index 3039af02bbb..50a52cecda9 100644 --- a/app/controllers/concerns/floc_opt_out.rb +++ b/app/controllers/concerns/floc_opt_out.rb @@ -4,7 +4,7 @@ module FlocOptOut extend ActiveSupport::Concern included do - after_action :set_floc_opt_out_header, unless: :floc_enabled? + before_action :set_floc_opt_out_header, unless: :floc_enabled? end def floc_enabled? diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index eae087bca4d..ae90bd59d01 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -17,10 +17,6 @@ module IssuableActions def show respond_to do |format| format.html do - @show_crm_contacts = issuable.is_a?(Issue) && # rubocop:disable Gitlab/ModuleWithInstanceVariables - can?(current_user, :read_crm_contact, issuable.project.group) && - CustomerRelations::Contact.exists_for_group?(issuable.project.group) - @issuable_sidebar = serializer.represent(issuable, serializer: 'sidebar') # rubocop:disable Gitlab/ModuleWithInstanceVariables render 'show' end @@ -43,18 +39,18 @@ module IssuableActions if updated_issuable.is_a?(Spammable) respond_to do |format| format.html do - # NOTE: This redirect is intentionally only performed in the case where the updated - # issuable is a spammable, and intentionally is not performed in the non-spammable case. - # This preserves the legacy behavior of this action. if updated_issuable.valid? + # NOTE: This redirect is intentionally only performed in the case where the valid updated + # issuable is a spammable, and intentionally is not performed below in the + # valid non-spammable case. This preserves the legacy behavior of this action. redirect_to spammable_path else - with_captcha_check_html_format { render :edit } + with_captcha_check_html_format(spammable: spammable) { render :edit } end end format.json do - with_captcha_check_json_format { render_entity_json } + with_captcha_check_json_format(spammable: spammable) { render_entity_json } end end else diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index b68db0e3f9f..96cf6021ea9 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -17,7 +17,7 @@ module IssuableCollectionsAction respond_to do |format| format.html - format.atom { render layout: 'xml.atom' } + format.atom { render layout: 'xml' } end end diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index f716c1f6c2f..0b9024dc3db 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -4,17 +4,6 @@ module MembershipActions include MembersPresentation extend ActiveSupport::Concern - def create - create_params = params.permit(:user_ids, :access_level, :expires_at) - result = Members::CreateService.new(current_user, create_params.merge({ source: membershipable, invite_source: "#{plain_source_type}-members-page" })).execute - - if result[:status] == :success - redirect_to members_page_url, notice: _('Users were successfully added.') - else - redirect_to members_page_url, alert: result[:message] - end - end - def update update_params = params.require(root_params_key).permit(:access_level, :expires_at) member = membershipable.members_and_requesters.find(params[:id]) @@ -79,8 +68,8 @@ module MembershipActions notice: _('Your request for access has been queued for review.') else redirect_to polymorphic_path(membershipable), - alert: _("Your request for access could not be processed: %{error_meesage}") % - { error_meesage: access_requester.errors.full_messages.to_sentence } + alert: _("Your request for access could not be processed: %{error_message}") % + { error_message: access_requester.errors.full_messages.to_sentence } end end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb new file mode 100644 index 00000000000..03296d6b233 --- /dev/null +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ProductAnalyticsTracking + include Gitlab::Tracking::Helpers + include RedisTracking + extend ActiveSupport::Concern + + class_methods do + def track_event(*controller_actions, name:, conditions: nil, destinations: [:redis_hll], &block) + custom_conditions = [:trackable_html_request?, *conditions] + + after_action only: controller_actions, if: custom_conditions do + route_events_to(destinations, name, &block) + end + end + end + + private + + def route_events_to(destinations, name, &block) + track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) + + if destinations.include?(:snowplow) && Feature.enabled?(:route_hll_to_snowplow, tracking_namespace_source, default_enabled: :yaml) + Gitlab::Tracking.event(self.class.to_s, name, namespace: tracking_namespace_source, user: current_user) + end + end +end diff --git a/app/controllers/concerns/search_rate_limitable.rb b/app/controllers/concerns/search_rate_limitable.rb new file mode 100644 index 00000000000..a77ebd276b6 --- /dev/null +++ b/app/controllers/concerns/search_rate_limitable.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module SearchRateLimitable + extend ActiveSupport::Concern + + private + + def check_search_rate_limit! + if current_user + check_rate_limit!(:search_rate_limit, scope: [current_user]) + else + check_rate_limit!(:search_rate_limit_unauthenticated, scope: [request.ip]) + end + end +end diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index 1f17f9f4e1b..48daacc09c2 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -26,6 +26,9 @@ module SessionlessAuthentication # for every request. If you want the token to work as a # sign in token, you can simply remove store: false. sign_in(user, store: false, message: :sessionless_sign_in) + elsif request_authenticator.can_sign_in_bot?(user) + # we suppress callbacks to avoid redirecting the bot + sign_in(user, store: false, message: :sessionless_sign_in, run_callbacks: false) end end diff --git a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb index 234c591ffb7..044519004b2 100644 --- a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb +++ b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb @@ -2,7 +2,6 @@ module SpammableActions::AkismetMarkAsSpamAction extend ActiveSupport::Concern - include SpammableActions::Attributes included do before_action :authorize_submit_spammable!, only: :mark_as_spam @@ -22,7 +21,15 @@ module SpammableActions::AkismetMarkAsSpamAction access_denied! unless current_user.can_admin_all_resources? end + def spammable + # The class extending this module should define the #spammable method to return + # the Spammable model instance via: `alias_method :spammable , <:model_name>` + raise NotImplementedError, "#{self.class} should implement #{__method__}" + end + def spammable_path - raise NotImplementedError, "#{self.class} does not implement #{__method__}" + # The class extending this module should define the #spammable_path method to return + # the route helper pointing to the action to show the Spammable instance + raise NotImplementedError, "#{self.class} should implement #{__method__}" end end diff --git a/app/controllers/concerns/spammable_actions/attributes.rb b/app/controllers/concerns/spammable_actions/attributes.rb deleted file mode 100644 index d7060e47c07..00000000000 --- a/app/controllers/concerns/spammable_actions/attributes.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module SpammableActions - module Attributes - extend ActiveSupport::Concern - - private - - def spammable - raise NotImplementedError, "#{self.class} does not implement #{__method__}" - end - end -end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/common.rb b/app/controllers/concerns/spammable_actions/captcha_check/common.rb index 7c047e02a1d..aaeb6b3ba83 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/common.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/common.rb @@ -1,23 +1,25 @@ # frozen_string_literal: true -module SpammableActions::CaptchaCheck - module Common - extend ActiveSupport::Concern +module SpammableActions + module CaptchaCheck + module Common + extend ActiveSupport::Concern - private + private - def with_captcha_check_common(captcha_render_lambda:, &block) - # If the Spammable indicates that CAPTCHA is not necessary (either due to it not being flagged - # as spam, or if spam/captcha is disabled for some reason), then we will go ahead and - # yield to the block containing the action's original behavior, then return. - return yield unless spammable.render_recaptcha? + def with_captcha_check_common(spammable:, captcha_render_lambda:, &block) + # If the Spammable indicates that CAPTCHA is not necessary (either due to it not being flagged + # as spam, or if spam/captcha is disabled for some reason), then we will go ahead and + # yield to the block containing the action's original behavior, then return. + return yield unless spammable.render_recaptcha? - # If we got here, we need to render the CAPTCHA instead of yielding to action's original - # behavior. We will present a CAPTCHA to be solved by executing the lambda which was passed - # as the `captcha_render_lambda:` argument. This lambda contains either the HTML-specific or - # JSON-specific behavior to cause the CAPTCHA modal to be rendered. - Gitlab::Recaptcha.load_configurations! - captcha_render_lambda.call + # If we got here, we need to render the CAPTCHA instead of yielding to action's original + # behavior. We will present a CAPTCHA to be solved by executing the lambda which was passed + # as the `captcha_render_lambda:` argument. This lambda contains either the HTML-specific or + # JSON-specific behavior to cause the CAPTCHA modal to be rendered. + Gitlab::Recaptcha.load_configurations! + captcha_render_lambda.call + end end end end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb index f687c0fcf2d..b254916cdd6 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb @@ -8,7 +8,6 @@ # which supports JSON format should be used instead. module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport extend ActiveSupport::Concern - include SpammableActions::Attributes include SpammableActions::CaptchaCheck::Common included do @@ -17,9 +16,9 @@ module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport private - def with_captcha_check_html_format(&block) + def with_captcha_check_html_format(spammable:, &block) captcha_render_lambda = -> { render :captcha_check } - with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block) + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) end # Convert spam/CAPTCHA values from form field params to headers, because all spam-related services diff --git a/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb index 0bfea05abc7..4a278a7b233 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb @@ -4,22 +4,27 @@ # In other words, forms handled by actions which use a `respond_to` of `format.js` or `format.json`. # # For example, for all Javascript based form submissions and Vue components which use Apollo and Axios +# which are directly handled by a controller other than `GraphqlController`. For example, issue +# update currently uses this module. +# +# However, requests which directly hit `GraphqlController` will not use this module - the +# `Mutations::SpamProtection` module handles those requests (for example, snippet create/update +# requests) # # If the request is handled by actions via `format.html`, then the corresponding module which # supports HTML format should be used instead. module SpammableActions::CaptchaCheck::JsonFormatActionsSupport extend ActiveSupport::Concern - include SpammableActions::Attributes include SpammableActions::CaptchaCheck::Common include Spam::Concerns::HasSpamActionResponseFields private - def with_captcha_check_json_format(&block) + def with_captcha_check_json_format(spammable:, &block) # NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response # which requires a CAPTCHA to be solved in order for the request to be resubmitted. # https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10 captcha_render_lambda = -> { render json: spam_action_response_fields(spammable), status: :conflict } - with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block) + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) end end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb new file mode 100644 index 00000000000..2ebfa90e6da --- /dev/null +++ b/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# This module should be included to support CAPTCHA check for REST API actions via Grape. +# +# If the request is directly handled by a controller action, then the corresponding module which +# supports HTML or JSON formats should be used instead. +module SpammableActions::CaptchaCheck::RestApiActionsSupport + extend ActiveSupport::Concern + include SpammableActions::CaptchaCheck::Common + include Spam::Concerns::HasSpamActionResponseFields + + private + + def with_captcha_check_rest_api(spammable:, &block) + # In the case of the REST API, the request is handled by Grape, so if there is a spam-related + # error, we don't render directly, instead we will pass the error message and other necessary + # fields to the Grape api error helper for it to handle. + captcha_render_lambda = -> do + fields = spam_action_response_fields(spammable) + + fields.delete :spam + # NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response + # which requires a CAPTCHA to be solved in order for the request to be resubmitted. + # https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10 + status = 409 + + # NOTE: This nested 'error' key may not be consistent with all other API error responses, + # because they are not currently consistent across different API endpoints + # and models. Some (snippets) will nest errors in an errors key like this, + # while others (issues) will return the model's errors hash without an errors key, + # while still others just return a plain string error. + # See https://gitlab.com/groups/gitlab-org/-/epics/5527#revisit-inconsistent-shape-of-error-responses-in-rest-api + fields[:message] = { error: spammable.errors.full_messages.to_sentence } + render_structured_api_error!(fields, status) + end + + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) + end +end diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index e1bfe92f61b..c9b6e8923fe 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -143,7 +143,7 @@ module UploadsActions end def bypass_auth_checks_on_uploads? - if ::Feature.enabled?(:enforce_auth_checks_on_uploads, default_enabled: :yaml) + if ::Feature.enabled?(:enforce_auth_checks_on_uploads, project, default_enabled: :yaml) false else action_name == 'show' && embeddable? |