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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
require 'digest/md5'
class Key < ActiveRecord::Base
include AfterCommitQueue
include Gitlab::CurrentSettings
include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user
before_validation :generate_fingerprint
validates :title,
presence: true,
length: { maximum: 255 }
validates :key,
presence: true,
length: { maximum: 5000 },
format: { with: /\A(ssh|ecdsa)-.*\Z/ }
validates :key,
format: { without: /\n|\r/, message: 'should be a single line' }
validates :fingerprint,
uniqueness: true,
presence: { message: 'cannot be generated' }
validate :key_meets_minimum_bit_length, :key_type_is_allowed
delegate :name, :email, to: :user, prefix: true
after_create :add_to_shell
after_create :notify_user
after_create :post_create_hook
after_destroy :remove_from_shell
after_destroy :post_destroy_hook
def key=(value)
value.strip! unless value.blank?
write_attribute(:key, value)
end
def publishable_key
# Strip out the keys comment so we don't leak email addresses
# Replace with simple ident of user_name (hostname)
self.key.split[0..1].push("#{self.user_name} (#{Gitlab.config.gitlab.host})").join(' ')
end
# projects that has this key
def projects
user.authorized_projects
end
def shell_id
"key-#{id}"
end
def update_last_used_at
lease = Gitlab::ExclusiveLease.new("key_update_last_used_at:#{id}", timeout: LAST_USED_AT_REFRESH_TIME)
return unless lease.try_obtain
UseKeyWorker.perform_async(id)
end
def add_to_shell
GitlabShellWorker.perform_async(
:add_key,
shell_id,
key
)
end
def post_create_hook
SystemHooksService.new.execute_hooks_for(self, :create)
end
def remove_from_shell
GitlabShellWorker.perform_async(
:remove_key,
shell_id,
key,
)
end
def post_destroy_hook
SystemHooksService.new.execute_hooks_for(self, :destroy)
end
private
def generate_fingerprint
self.fingerprint = nil
return unless self.key.present?
self.fingerprint = public_key.fingerprint
end
def key_meets_minimum_bit_length
return unless key?
case public_key.type
when :ecdsa
if public_key.size < current_application_settings.minimum_ecdsa_bits
errors.add(:key, "elliptic curve size must be at least #{current_application_settings.minimum_ecdsa_bits} bits")
end
when :rsa
if public_key.size < current_application_settings.minimum_rsa_bits
errors.add(:key, "length must be at least #{current_application_settings.minimum_rsa_bits} bits")
end
end
end
def key_type_is_allowed
return unless key?
unless current_application_settings.allowed_key_types.include?(public_key.type)
allowed_types = current_application_settings.allowed_key_types.to_sentence(last_word_connector: ', or ', two_words_connector: ' or ')
errors.add(:key, "type is not allowed. Must be #{allowed_types}")
end
end
def public_key
@public_key ||= Gitlab::SSHPublicKey.new(key)
end
def notify_user
run_after_commit { NotificationService.new.new_key(self) }
end
end
|