summaryrefslogtreecommitdiff
path: root/app/models/concerns/spammable.rb
blob: 4fbb5dcb6493f16d103d126d4b018b5e9715f1de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# frozen_string_literal: true

module Spammable
  extend ActiveSupport::Concern

  class_methods do
    def attr_spammable(attr, options = {})
      spammable_attrs << [attr.to_s, options]
    end
  end

  included do
    has_one :user_agent_detail, as: :subject, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent

    attr_accessor :spam
    attr_accessor :spam_log
    alias_method :spam?, :spam

    after_validation :invalidate_if_spam, on: [:create, :update]

    cattr_accessor :spammable_attrs, instance_accessor: false do
      []
    end

    delegate :ip_address, :user_agent, to: :user_agent_detail, allow_nil: true
  end

  def submittable_as_spam_by?(current_user)
    current_user && current_user.admin? && submittable_as_spam?
  end

  def submittable_as_spam?
    if user_agent_detail
      user_agent_detail.submittable? && Gitlab::CurrentSettings.current_application_settings.akismet_enabled
    else
      false
    end
  end

  def needs_recaptcha!
    self.errors.add(:base, "Your #{spammable_entity_type} has been recognized as spam. "\
                  "Please, change the content or solve the reCAPTCHA to proceed.")
  end

  def unrecoverable_spam_error!
    self.errors.add(:base, "Your #{spammable_entity_type} has been recognized as spam and has been discarded.")
  end

  def invalidate_if_spam
    return unless spam?

    if Gitlab::Recaptcha.enabled?
      needs_recaptcha!
    else
      unrecoverable_spam_error!
    end
  end

  def spammable_entity_type
    self.class.name.underscore
  end

  def spam_title
    attr = self.class.spammable_attrs.find do |_, options|
      options.fetch(:spam_title, false)
    end

    public_send(attr.first) if attr && respond_to?(attr.first.to_sym) # rubocop:disable GitlabSecurity/PublicSend
  end

  def spam_description
    attr = self.class.spammable_attrs.find do |_, options|
      options.fetch(:spam_description, false)
    end

    public_send(attr.first) if attr && respond_to?(attr.first.to_sym) # rubocop:disable GitlabSecurity/PublicSend
  end

  def spammable_text
    result = self.class.spammable_attrs.map do |attr|
      public_send(attr.first) # rubocop:disable GitlabSecurity/PublicSend
    end

    result.reject(&:blank?).join("\n")
  end

  # Override in Spammable if further checks are necessary
  def check_for_spam?
    true
  end

  # Override in Spammable if differs
  def allow_possible_spam?
    Feature.enabled?(:allow_possible_spam, project)
  end
end