summaryrefslogtreecommitdiff
path: root/app/models/service_desk/custom_email_verification.rb
blob: 69bd0f5e7f845b543287017c11687ba03a853839 (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
97
98
99
100
101
102
103
104
105
# frozen_string_literal: true

module ServiceDesk
  class CustomEmailVerification < ApplicationRecord
    TIMEFRAME = 30.minutes
    STATES = { started: 0, finished: 1, failed: 2 }.freeze

    enum error: {
      incorrect_token: 0,
      incorrect_from: 1,
      mail_not_received_within_timeframe: 2,
      invalid_credentials: 3,
      smtp_host_issue: 4
    }

    attr_encrypted :token,
      mode: :per_attribute_iv,
      algorithm: 'aes-256-gcm',
      key: Settings.attr_encrypted_db_key_base_32,
      encode: false,
      encode_iv: false

    belongs_to :project
    belongs_to :triggerer, class_name: 'User', optional: true

    validates :project, presence: true
    validates :state, presence: true

    delegate :service_desk_setting, to: :project

    state_machine :state do
      state :started do
        validates :token, presence: true, length: { is: 12 }
        validates :triggerer, presence: true
        validates :triggered_at, presence: true
        validates :error, absence: true
      end

      state :finished do
        validates :token, absence: true
        validates :error, absence: true
      end

      state :failed do
        validates :token, absence: true
        validates :error, presence: true
      end

      event :mark_as_started do
        transition all => :started
      end

      event :mark_as_finished do
        transition started: :finished
      end

      event :mark_as_failed do
        transition all => :failed
      end

      before_transition any => :started do |verification, transition|
        triggerer = transition.args.first

        verification.triggerer = triggerer
        verification.token = verification.class.generate_token
        verification.triggered_at = Time.current
        verification.error = nil
      end

      before_transition started: :finished do |verification|
        verification.token = nil
      end

      before_transition started: :failed do |verification, transition|
        error = transition.args.first

        verification.error = error
        verification.token = nil
      end
    end

    # Needs to be below `state_machine` definition to suppress
    # its method override warnings
    enum state: STATES

    class << self
      def generate_token
        SecureRandom.alphanumeric(12)
      end
    end

    def accepted_until
      return unless started?
      return unless triggered_at.present?

      TIMEFRAME.since(triggered_at)
    end

    def in_timeframe?
      return false unless started?

      !!accepted_until&.future?
    end
  end
end