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
129
130
|
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
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
when :dsa
if public_key.size < current_application_settings.minimum_dsa_bits
errors.add(:key, "length must be at least #{current_application_settings.minimum_dsa_bits} bits")
end
end
end
def key_type_is_allowed
unless current_application_settings.allowed_key_types.include?(public_key.type.to_s)
allowed_types = current_application_settings.allowed_key_types.
map(&:upcase).
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
|