2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2014-09-02 18:07:02 +05:30
require 'digest/md5'
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
2020-01-01 13:55:28 +05:30
include Sha256Attribute
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
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
2017-08-17 22:00:37 +05:30
validates :fingerprint ,
uniqueness : true ,
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
2014-09-02 18:07:02 +05:30
delegate :name , :email , to : :user , prefix : true
2020-04-08 14:13:33 +05:30
after_commit :add_to_authorized_keys , on : :create
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
2020-04-08 14:13:33 +05:30
after_commit :remove_from_authorized_keys , on : :destroy
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
2014-09-02 18:07:02 +05:30
2020-01-01 13:55:28 +05:30
alias_attribute :fingerprint_md5 , :fingerprint
scope :preload_users , - > { preload ( :user ) }
scope :for_user , - > ( user ) { where ( user : user ) }
scope :order_last_used_at_desc , - > { reorder ( :: Gitlab :: Database . nulls_last_order ( 'last_used_at' , 'DESC' ) ) }
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
2018-03-17 18:26:18 +05:30
Keys :: LastUsedService . new ( self ) . execute
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
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
2020-01-01 13:55:28 +05:30
self . fingerprint_md5 = public_key . fingerprint
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
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
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' )