debian-mirror-gitlab/app/services/spam/spam_action_service.rb

138 lines
4.9 KiB
Ruby
Raw Normal View History

2020-05-24 23:13:21 +05:30
# frozen_string_literal: true
module Spam
class SpamActionService
include SpamConstants
2021-09-30 23:02:18 +05:30
def initialize(spammable:, spam_params:, user:, action:)
2020-05-24 23:13:21 +05:30
@target = spammable
2021-09-30 23:02:18 +05:30
@spam_params = spam_params
2020-06-23 00:09:42 +05:30
@user = user
2021-03-11 19:13:27 +05:30
@action = action
end
2020-05-24 23:13:21 +05:30
2021-04-29 21:17:54 +05:30
# rubocop:disable Metrics/AbcSize
2021-09-30 23:02:18 +05:30
def execute
# If spam_params is passed as `nil`, no check will be performed. This is the easiest way to allow
# composed services which may not need to do spam checking to "opt out". For example, when
# MoveService is calling CreateService, spam checking is not necessary, as no new content is
# being created.
return ServiceResponse.success(message: 'Skipped spam check because spam_params was not present') unless spam_params
2020-05-24 23:13:21 +05:30
2021-09-30 23:02:18 +05:30
recaptcha_verified = Captcha::CaptchaVerificationService.new(spam_params: spam_params).execute
2021-03-11 19:13:27 +05:30
2020-05-24 23:13:21 +05:30
if recaptcha_verified
2021-04-29 21:17:54 +05:30
# If it's a request which is already verified through CAPTCHA,
2020-05-24 23:13:21 +05:30
# update the spam log accordingly.
2021-03-11 19:13:27 +05:30
SpamLog.verify_recaptcha!(user_id: user.id, id: spam_params.spam_log_id)
2021-04-29 21:17:54 +05:30
ServiceResponse.success(message: "CAPTCHA successfully verified")
2020-05-24 23:13:21 +05:30
else
2021-03-11 19:13:27 +05:30
return ServiceResponse.success(message: 'Skipped spam check because user was allowlisted') if allowlisted?(user)
return ServiceResponse.success(message: 'Skipped spam check because it was not required') unless check_for_spam?
2020-05-24 23:13:21 +05:30
2021-09-30 23:02:18 +05:30
perform_spam_service_check
2021-04-29 21:17:54 +05:30
ServiceResponse.success(message: "Spam check performed. Check #{target.class.name} spammable model for any errors or CAPTCHA requirement")
2020-05-24 23:13:21 +05:30
end
end
2021-04-29 21:17:54 +05:30
# rubocop:enable Metrics/AbcSize
2020-05-24 23:13:21 +05:30
delegate :check_for_spam?, to: :target
private
2021-09-30 23:02:18 +05:30
attr_reader :user, :action, :target, :spam_params, :spam_log
2021-03-11 19:13:27 +05:30
##
# In order to be proceed to the spam check process, the target must be
# a dirty instance, which means it should be already assigned with the new
# attribute values.
def ensure_target_is_dirty
msg = "Target instance of #{target.class.name} must be dirty (must have changes to save)"
raise(msg) unless target.has_changes_to_save?
end
2020-06-23 00:09:42 +05:30
2020-05-24 23:13:21 +05:30
def allowlisted?(user)
2021-01-03 14:25:43 +05:30
user.try(:gitlab_employee?) || user.try(:gitlab_bot?) || user.try(:gitlab_service_user?)
2020-05-24 23:13:21 +05:30
end
2021-03-11 19:13:27 +05:30
##
# Performs the spam check using the spam verdict service, and modifies the target model
# accordingly based on the result.
2021-09-30 23:02:18 +05:30
def perform_spam_service_check
2021-03-11 19:13:27 +05:30
ensure_target_is_dirty
2020-05-24 23:13:21 +05:30
# since we can check for spam, and recaptcha is not verified,
# ask the SpamVerdictService what to do with the target.
spam_verdict_service.execute.tap do |result|
case result
2020-06-23 00:09:42 +05:30
when CONDITIONAL_ALLOW
# at the moment, this means "ask for reCAPTCHA"
2021-09-30 23:02:18 +05:30
create_spam_log
2020-05-24 23:13:21 +05:30
break if target.allow_possible_spam?
target.needs_recaptcha!
when DISALLOW
# TODO: remove `unless target.allow_possible_spam?` once this flag has been passed to `SpamVerdictService`
# https://gitlab.com/gitlab-org/gitlab/-/issues/214739
target.spam! unless target.allow_possible_spam?
2021-09-30 23:02:18 +05:30
create_spam_log
2021-06-08 01:23:25 +05:30
when BLOCK_USER
# TODO: improve BLOCK_USER handling, non-existent until now
# https://gitlab.com/gitlab-org/gitlab/-/issues/329666
target.spam! unless target.allow_possible_spam?
2021-09-30 23:02:18 +05:30
create_spam_log
2020-05-24 23:13:21 +05:30
when ALLOW
target.clear_spam_flags!
2021-06-08 01:23:25 +05:30
when NOOP
# spamcheck is not explicitly rendering a verdict & therefore can't make a decision
target.clear_spam_flags!
2020-05-24 23:13:21 +05:30
end
end
end
2021-09-30 23:02:18 +05:30
def create_spam_log
2020-05-24 23:13:21 +05:30
@spam_log = SpamLog.create!(
{
user_id: target.author_id,
title: target.spam_title,
description: target.spam_description,
2021-09-30 23:02:18 +05:30
source_ip: spam_params.ip_address,
user_agent: spam_params.user_agent,
2021-03-11 19:13:27 +05:30
noteable_type: noteable_type,
2021-09-30 23:02:18 +05:30
# Now, all requests are via the API, so hardcode it to true to simplify the logic and API
# of this service. See https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2266
# for original introduction of `via_api` field.
# See discussion here about possibly deprecating this field:
# https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2266#note_542527450
via_api: true
2020-05-24 23:13:21 +05:30
}
)
target.spam_log = spam_log
end
def spam_verdict_service
2021-03-11 19:13:27 +05:30
context = {
action: action,
target_type: noteable_type
}
2021-09-30 23:02:18 +05:30
options = {
ip_address: spam_params.ip_address,
user_agent: spam_params.user_agent,
referer: spam_params.referer
}
2020-05-24 23:13:21 +05:30
SpamVerdictService.new(target: target,
2020-06-23 00:09:42 +05:30
user: user,
options: options,
2021-03-11 19:13:27 +05:30
context: context)
2020-06-23 00:09:42 +05:30
end
2021-03-11 19:13:27 +05:30
def noteable_type
2020-06-23 00:09:42 +05:30
@notable_type ||= target.class.to_s
2020-05-24 23:13:21 +05:30
end
end
end