66 lines
3 KiB
Ruby
66 lines
3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Spam
|
|
##
|
|
# This class is a Parameter Object (https://refactoring.com/catalog/introduceParameterObject.html)
|
|
# which acts as an container abstraction for multiple values related to spam and
|
|
# captcha processing for a provided HTTP request object.
|
|
#
|
|
# It is used to encapsulate these values and allow them to be passed from the Controller/GraphQL
|
|
# layers down into to the Service layer, without needing to pass the entire request and therefore
|
|
# unnecessarily couple the Service layer to the HTTP request.
|
|
#
|
|
# Values contained are:
|
|
#
|
|
# captcha_response: The response resulting from the user solving a captcha. Currently it is
|
|
# a scalar reCAPTCHA response string, but it can be expanded to an object in the future to
|
|
# support other captcha implementations such as FriendlyCaptcha. Obtained from
|
|
# request.headers['X-GitLab-Captcha-Response']
|
|
# spam_log_id: The id of a SpamLog record. Obtained from request.headers['X-GitLab-Spam-Log-Id']
|
|
# ip_address = The remote IP. Obtained from request.env['action_dispatch.remote_ip']
|
|
# user_agent = The user agent. Obtained from request.env['HTTP_USER_AGENT']
|
|
# referer = The HTTP referer. Obtained from request.env['HTTP_REFERER']
|
|
#
|
|
# NOTE: The presence of these values in the request is not currently enforced. If they are missing,
|
|
# then the spam check may fail, or the SpamLog or UserAgentDetail may have missing fields.
|
|
class SpamParams
|
|
def self.new_from_request(request:)
|
|
self.normalize_grape_request_headers(request: request)
|
|
self.new(
|
|
captcha_response: request.headers['X-GitLab-Captcha-Response'],
|
|
spam_log_id: request.headers['X-GitLab-Spam-Log-Id'],
|
|
ip_address: request.env['action_dispatch.remote_ip'].to_s,
|
|
user_agent: request.env['HTTP_USER_AGENT'],
|
|
referer: request.env['HTTP_REFERER']
|
|
)
|
|
end
|
|
|
|
attr_reader :captcha_response, :spam_log_id, :ip_address, :user_agent, :referer
|
|
|
|
def initialize(captcha_response:, spam_log_id:, ip_address:, user_agent:, referer:)
|
|
@captcha_response = captcha_response
|
|
@spam_log_id = spam_log_id
|
|
@ip_address = ip_address
|
|
@user_agent = user_agent
|
|
@referer = referer
|
|
end
|
|
|
|
def ==(other)
|
|
other.class <= self.class &&
|
|
other.captcha_response == captcha_response &&
|
|
other.spam_log_id == spam_log_id &&
|
|
other.ip_address == ip_address &&
|
|
other.user_agent == user_agent &&
|
|
other.referer == referer
|
|
end
|
|
|
|
def self.normalize_grape_request_headers(request:)
|
|
# If needed, make a normalized copy of Grape headers with the case of 'GitLab' (with an
|
|
# uppercase 'L') instead of 'Gitlab' (with a lowercase 'l'), because Grape header helper keys
|
|
# are "coerced into a capitalized kebab case". See https://github.com/ruby-grape/grape#request
|
|
%w[X-Gitlab-Captcha-Response X-Gitlab-Spam-Log-Id].each do |header|
|
|
request.headers[header.gsub('Gitlab', 'GitLab')] = request.headers[header] if request.headers.key?(header)
|
|
end
|
|
end
|
|
end
|
|
end
|