2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2019-07-07 11:18:12 +05:30
class Key < ApplicationRecord
2018-03-17 18:26:18 +05:30
include AfterCommitQueue
2015-04-26 12:48:37 +05:30
include Sortable
2022-07-16 23:28:13 +05:30
include ShaAttribute
2020-04-08 14:13:33 +05:30
include Expirable
2021-09-04 01:27:46 +05:30
include FromUnion
2020-01-01 13:55:28 +05:30
sha256_attribute :fingerprint_sha256
2014-09-02 18:07:02 +05:30
2017-08-17 22:00:37 +05:30
belongs_to :user
2014-09-02 18:07:02 +05:30
2023-04-23 21:23:45 +05:30
has_many :ssh_signatures , class_name : 'CommitSignatures::SshSignature'
2017-08-17 22:00:37 +05:30
before_validation :generate_fingerprint
validates :title ,
presence : true ,
length : { maximum : 255 }
2018-03-17 18:26:18 +05:30
2017-08-17 22:00:37 +05:30
validates :key ,
presence : true ,
length : { maximum : 5000 } ,
2022-03-02 08:16:31 +05:30
format : { with : / \ A( #{ Gitlab :: SSHPublicKey . supported_algorithms . join ( '|' ) } ) / }
2018-03-17 18:26:18 +05:30
2022-06-21 17:19:12 +05:30
validates :fingerprint_sha256 ,
uniqueness : true ,
2022-07-16 23:28:13 +05:30
presence : { message : 'cannot be generated' }
2014-09-02 18:07:02 +05:30
2018-03-17 18:26:18 +05:30
validate :key_meets_restrictions
2022-07-16 23:28:13 +05:30
validate :expiration , on : :create
2022-08-13 15:12:31 +05:30
validate :banned_key , if : :key_changed?
2018-03-17 18:26:18 +05:30
2014-09-02 18:07:02 +05:30
delegate :name , :email , to : :user , prefix : true
2023-03-04 22:38:38 +05:30
enum usage_type : {
auth_and_signing : 0 ,
auth : 1 ,
signing : 2
}
2015-04-26 12:48:37 +05:30
after_create :post_create_hook
2018-03-17 18:26:18 +05:30
after_create :refresh_user_cache
2015-04-26 12:48:37 +05:30
after_destroy :post_destroy_hook
2018-03-17 18:26:18 +05:30
after_destroy :refresh_user_cache
2023-03-04 22:38:38 +05:30
after_commit :add_to_authorized_keys , on : :create
after_commit :remove_from_authorized_keys , on : :destroy
2014-09-02 18:07:02 +05:30
2020-01-01 13:55:28 +05:30
alias_attribute :fingerprint_md5 , :fingerprint
2022-08-27 11:52:29 +05:30
alias_attribute :name , :title
2020-01-01 13:55:28 +05:30
scope :preload_users , - > { preload ( :user ) }
scope :for_user , - > ( user ) { where ( user : user ) }
2022-06-21 17:19:12 +05:30
scope :order_last_used_at_desc , - > { reorder ( arel_table [ :last_used_at ] . desc . nulls_last ) }
2023-03-04 22:38:38 +05:30
scope :auth , - > { where ( usage_type : [ :auth , :auth_and_signing ] ) }
scope :signing , - > { where ( usage_type : [ :signing , :auth_and_signing ] ) }
2021-09-04 01:27:46 +05:30
# Date is set specifically in this scope to improve query time.
2021-12-11 22:18:48 +05:30
scope :expired_today_and_not_notified , - > { where ( [ " date(expires_at AT TIME ZONE 'UTC') = CURRENT_DATE AND expiry_notification_delivered_at IS NULL " ] ) }
2021-04-29 21:17:54 +05:30
scope :expiring_soon_and_not_notified , - > { where ( [ " date(expires_at AT TIME ZONE 'UTC') > CURRENT_DATE AND date(expires_at AT TIME ZONE 'UTC') < ? AND before_expiry_notification_delivered_at IS NULL " , DAYS_TO_EXPIRE . days . from_now . to_date ] ) }
2020-01-01 13:55:28 +05:30
2018-12-13 13:39:08 +05:30
def self . regular_keys
where ( type : [ 'Key' , nil ] )
end
2017-08-17 22:00:37 +05:30
def key = ( value )
2018-03-27 19:54:05 +05:30
write_attribute ( :key , value . present? ? Gitlab :: SSHPublicKey . sanitize ( value ) : nil )
2018-03-17 18:26:18 +05:30
@public_key = nil
2014-09-02 18:07:02 +05:30
end
2015-09-11 14:41:01 +05:30
def publishable_key
2016-09-13 17:45:13 +05:30
# 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 ( ' ' )
2015-09-11 14:41:01 +05:30
end
2014-09-02 18:07:02 +05:30
# projects that has this key
def projects
user . authorized_projects
end
def shell_id
" key- #{ id } "
end
2019-09-04 21:01:54 +05:30
# EE overrides this
def can_delete?
true
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2017-08-17 22:00:37 +05:30
def update_last_used_at
2023-07-09 08:55:56 +05:30
Keys :: LastUsedService . new ( self ) . execute_async
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2017-08-17 22:00:37 +05:30
2020-04-08 14:13:33 +05:30
def add_to_authorized_keys
return unless Gitlab :: CurrentSettings . authorized_keys_enabled?
AuthorizedKeysWorker . perform_async ( :add_key , shell_id , key )
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2015-04-26 12:48:37 +05:30
def post_create_hook
SystemHooksService . new . execute_hooks_for ( self , :create )
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2015-04-26 12:48:37 +05:30
2020-04-08 14:13:33 +05:30
def remove_from_authorized_keys
return unless Gitlab :: CurrentSettings . authorized_keys_enabled?
AuthorizedKeysWorker . perform_async ( :remove_key , shell_id )
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2018-03-17 18:26:18 +05:30
def refresh_user_cache
return unless user
Users :: KeysCountService . new ( user ) . refresh_cache
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2018-03-17 18:26:18 +05:30
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2015-04-26 12:48:37 +05:30
def post_destroy_hook
SystemHooksService . new . execute_hooks_for ( self , :destroy )
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2015-04-26 12:48:37 +05:30
2018-03-17 18:26:18 +05:30
def public_key
@public_key || = Gitlab :: SSHPublicKey . new ( key )
end
2022-08-13 15:12:31 +05:30
def ensure_sha256_fingerprint!
return if self . fingerprint_sha256
save if generate_fingerprint
end
2023-04-23 21:23:45 +05:30
def signing?
super || auth_and_signing?
end
2014-09-02 18:07:02 +05:30
private
2015-04-26 12:48:37 +05:30
def generate_fingerprint
2014-09-02 18:07:02 +05:30
self . fingerprint = nil
2020-01-01 13:55:28 +05:30
self . fingerprint_sha256 = nil
2015-04-26 12:48:37 +05:30
2018-03-27 19:54:05 +05:30
return unless public_key . valid?
2015-04-26 12:48:37 +05:30
2022-06-21 17:19:12 +05:30
self . fingerprint_md5 = public_key . fingerprint unless Gitlab :: FIPS . enabled?
2022-04-04 11:22:00 +05:30
self . fingerprint_sha256 = public_key . fingerprint_sha256 . gsub ( " SHA256: " , " " )
2014-09-02 18:07:02 +05:30
end
2017-08-17 22:00:37 +05:30
2018-03-17 18:26:18 +05:30
def key_meets_restrictions
restriction = Gitlab :: CurrentSettings . key_restriction_for ( public_key . type )
if restriction == ApplicationSetting :: FORBIDDEN_KEY_VALUE
errors . add ( :key , forbidden_key_type_message )
elsif public_key . bits < restriction
errors . add ( :key , " must be at least #{ restriction } bits " )
end
end
2022-07-23 23:45:48 +05:30
def banned_key
return unless public_key . banned?
help_page_url = Rails . application . routes . url_helpers . help_page_url (
'security/ssh_keys_restrictions' ,
anchor : 'block-banned-or-compromised-keys'
)
errors . add (
:key ,
_ ( 'cannot be used because it belongs to a compromised private key. Stop using this key and generate a new one.' ) ,
help_page_url : help_page_url
)
end
2018-03-17 18:26:18 +05:30
def forbidden_key_type_message
2020-03-13 15:44:24 +05:30
allowed_types = Gitlab :: CurrentSettings . allowed_key_types . map ( & :upcase )
2018-03-17 18:26:18 +05:30
2020-03-13 15:44:24 +05:30
" type is forbidden. Must be #{ Gitlab :: Utils . to_exclusive_sentence ( allowed_types ) } "
2017-08-17 22:00:37 +05:30
end
2022-07-16 23:28:13 +05:30
def expiration
errors . add ( :key , message : 'has expired' ) if expired?
end
2014-09-02 18:07:02 +05:30
end
2019-12-04 20:38:33 +05:30
2021-06-08 01:23:25 +05:30
Key . prepend_mod_with ( 'Key' )