gpg_key.rb 2.45 KB
Newer Older
Alexis Reigel's avatar
Alexis Reigel committed
1
2
3
class GpgKey < ActiveRecord::Base
  KEY_PREFIX = '-----BEGIN PGP PUBLIC KEY BLOCK-----'.freeze

4
5
6
7
8
  include ShaAttribute

  sha_attribute :primary_keyid
  sha_attribute :fingerprint

Alexis Reigel's avatar
Alexis Reigel committed
9
  belongs_to :user
10
  has_many :gpg_signatures, dependent: :nullify
Alexis Reigel's avatar
Alexis Reigel committed
11

12
13
  validates :user, presence: true

Alexis Reigel's avatar
Alexis Reigel committed
14
15
16
17
  validates :key,
    presence: true,
    uniqueness: true,
    format: {
18
19
      with: /\A#{KEY_PREFIX}((?!#{KEY_PREFIX}).)+\Z/m,
      message: "is invalid. A valid public GPG key begins with '#{KEY_PREFIX}'"
Alexis Reigel's avatar
Alexis Reigel committed
20
21
    }

22
23
24
25
26
27
28
  validates :fingerprint,
    presence: true,
    uniqueness: true,
    # only validate when the `key` is valid, as we don't want the user to show
    # the error about the fingerprint
    unless: -> { errors.has_key?(:key) }

29
30
31
32
33
34
35
36
  validates :primary_keyid,
    presence: true,
    uniqueness: true,
    # only validate when the `key` is valid, as we don't want the user to show
    # the error about the fingerprint
    unless: -> { errors.has_key?(:key) }

  before_validation :extract_fingerprint, :extract_primary_keyid
37
38
  after_commit :update_invalid_gpg_signatures, on: :create
  after_commit :notify_user, on: :create
Alexis Reigel's avatar
Alexis Reigel committed
39
40
41
42
43
44

  def key=(value)
    value.strip! unless value.blank?
    write_attribute(:key, value)
  end

45
46
47
48
49
50
51
52
  def user_infos
    @user_infos ||= Gitlab::Gpg.user_infos_from_key(key)
  end

  def verified_user_infos
    user_infos.select do |user_info|
      user_info[:email] == user.email
    end
Alexis Reigel's avatar
Alexis Reigel committed
53
54
  end

55
  def emails_with_verified_status
56
    user_infos.map do |user_info|
Alexis Reigel's avatar
Alexis Reigel committed
57
      [
58
59
        user_info[:email],
        user_info[:email] == user.email
Alexis Reigel's avatar
Alexis Reigel committed
60
      ]
Alexis Reigel's avatar
Alexis Reigel committed
61
    end.to_h
Alexis Reigel's avatar
Alexis Reigel committed
62
63
  end

64
65
66
67
  def verified?
    emails_with_verified_status.any? { |_email, verified| verified }
  end

68
  def update_invalid_gpg_signatures
69
    InvalidGpgSignatureUpdateWorker.perform_async(self.id)
70
71
  end

Alexis Reigel's avatar
Alexis Reigel committed
72
73
74
75
76
77
78
79
80
81
82
  def revoke
    GpgSignature.where(gpg_key: self, valid_signature: true).find_each do |gpg_signature|
      gpg_signature.update_attributes!(
        gpg_key: nil,
        valid_signature: false
      )
    end

    destroy
  end

Alexis Reigel's avatar
Alexis Reigel committed
83
84
85
86
87
  private

  def extract_fingerprint
    # we can assume that the result only contains one item as the validation
    # only allows one key
88
    self.fingerprint = Gitlab::Gpg.fingerprints_from_key(key).first
Alexis Reigel's avatar
Alexis Reigel committed
89
  end
90

91
92
93
94
95
96
  def extract_primary_keyid
    # we can assume that the result only contains one item as the validation
    # only allows one key
    self.primary_keyid = Gitlab::Gpg.primary_keyids_from_key(key).first
  end

97
  def notify_user
98
    NotificationService.new.new_gpg_key(self)
99
  end
Alexis Reigel's avatar
Alexis Reigel committed
100
end