summaryrefslogtreecommitdiff
path: root/app/controllers/concerns/spammable_actions/captcha_check
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers/concerns/spammable_actions/captcha_check')
-rw-r--r--app/controllers/concerns/spammable_actions/captcha_check/common.rb23
-rw-r--r--app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb35
-rw-r--r--app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb25
3 files changed, 83 insertions, 0 deletions
diff --git a/app/controllers/concerns/spammable_actions/captcha_check/common.rb b/app/controllers/concerns/spammable_actions/captcha_check/common.rb
new file mode 100644
index 00000000000..7c047e02a1d
--- /dev/null
+++ b/app/controllers/concerns/spammable_actions/captcha_check/common.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module SpammableActions::CaptchaCheck
+ module Common
+ extend ActiveSupport::Concern
+
+ 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?
+
+ # 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
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
new file mode 100644
index 00000000000..f687c0fcf2d
--- /dev/null
+++ b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+# This module should *ONLY* be included if needed to support forms submits with HTML MIME type.
+# In other words, forms handled by actions which use a `respond_to` of `format.html`.
+#
+# If the request is handled by actions via `format.json`, for example, for all Javascript based form
+# submissions and Vue components which use Apollo and Axios, then the corresponding module
+# which supports JSON format should be used instead.
+module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport
+ extend ActiveSupport::Concern
+ include SpammableActions::Attributes
+ include SpammableActions::CaptchaCheck::Common
+
+ included do
+ before_action :convert_html_spam_params_to_headers, only: [:create, :update]
+ end
+
+ private
+
+ def with_captcha_check_html_format(&block)
+ captcha_render_lambda = -> { render :captcha_check }
+ with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block)
+ end
+
+ # Convert spam/CAPTCHA values from form field params to headers, because all spam-related services
+ # expect these values to be passed as headers.
+ #
+ # The 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the
+ # recaptcha gem. This is a field which is automatically included by calling the
+ # `#recaptcha_tags` method within a HAML template's form.
+ def convert_html_spam_params_to_headers
+ request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] if params['g-recaptcha-response']
+ request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id] if params[:spam_log_id]
+ end
+end
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
new file mode 100644
index 00000000000..0bfea05abc7
--- /dev/null
+++ b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# This module should be included to support forms submits with a 'js' or 'json' type of MIME type.
+# 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
+#
+# 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)
+ # 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)
+ end
+end