# frozen_string_literal: true module Captcha ## # Encapsulates logic of checking captchas. # class CaptchaVerificationService include Recaptcha::Adapters::ControllerMethods # Currently the only value that is used out of the request by the reCAPTCHA library # is 'remote_ip'. Therefore, we just create a struct to avoid passing the full request # object through all the service layer objects, and instead just rely on passing only # the required remote_ip value. This eliminates the need to couple the service layer # to the HTTP request (for the purpose of this service, at least). RequestStruct = Struct.new(:remote_ip) def initialize(spam_params:) @spam_params = spam_params end ## # Performs verification of a captcha response. # # NOTE: Currently only supports reCAPTCHA, and is not yet used in all places of the app in which # captchas are verified, but these can be addressed in future MRs. See: # https://gitlab.com/gitlab-org/gitlab/-/issues/273480 def execute return false unless spam_params.captcha_response @request = RequestStruct.new(spam_params.ip_address) Gitlab::Recaptcha.load_configurations! # NOTE: We could pass the model and let the recaptcha gem automatically add errors to it, # but we do not, for two reasons: # # 1. We want control over when the errors are added # 2. We want control over the wording and i18n of the message # 3. We want a consistent interface and behavior when adding support for other captcha # libraries which may not support automatically adding errors to the model. verify_recaptcha(response: spam_params.captcha_response) end private attr_reader :spam_params # The recaptcha library's Recaptcha::Adapters::ControllerMethods#verify_recaptcha method requires that # 'request' be a readable attribute - it doesn't support passing it as an options argument. attr_reader :request end end