2018-07-25 05:30:33 -04:00
# frozen_string_literal: true
2012-02-07 16:56:53 -05:00
require 'digest/md5'
2019-03-28 09:17:42 -04:00
class Key < ApplicationRecord
2017-11-29 10:30:17 -05:00
include AfterCommitQueue
2015-02-05 17:20:55 -05:00
include Sortable
2019-12-11 13:08:10 -05:00
include Sha256Attribute
2020-03-13 05:09:23 -04:00
include Expirable
2021-06-09 20:10:09 -04:00
include FromUnion
2019-12-11 13:08:10 -05:00
sha256_attribute :fingerprint_sha256
2013-06-24 13:07:21 -04:00
2011-10-08 17:36:38 -04:00
belongs_to :user
2016-11-15 14:16:45 -05:00
before_validation :generate_fingerprint
2012-10-08 20:10:04 -04:00
2016-12-02 07:54:57 -05:00
validates :title ,
presence : true ,
length : { maximum : 255 }
2017-08-21 06:30:03 -04:00
2016-12-02 07:54:57 -05:00
validates :key ,
presence : true ,
length : { maximum : 5000 } ,
format : { with : / \ A(ssh|ecdsa)-.* \ Z / }
2017-08-21 06:30:03 -04:00
2016-12-02 07:54:57 -05:00
validates :fingerprint ,
uniqueness : true ,
presence : { message : 'cannot be generated' }
2011-10-08 17:36:38 -04:00
2017-08-25 09:08:48 -04:00
validate :key_meets_restrictions
2017-08-21 06:30:03 -04:00
2012-08-10 18:07:50 -04:00
delegate :name , :email , to : :user , prefix : true
2012-02-07 16:56:53 -05:00
2020-03-12 08:09:17 -04:00
after_commit :add_to_authorized_keys , on : :create
2014-08-18 10:46:46 -04:00
after_create :post_create_hook
2017-11-15 09:47:10 -05:00
after_create :refresh_user_cache
2020-03-12 08:09:17 -04:00
after_commit :remove_from_authorized_keys , on : :destroy
2014-08-18 10:46:46 -04:00
after_destroy :post_destroy_hook
2017-11-15 09:47:10 -05:00
after_destroy :refresh_user_cache
2014-04-02 14:13:05 -04:00
2019-12-11 13:08:10 -05:00
alias_attribute :fingerprint_md5 , :fingerprint
2019-12-17 04:07:48 -05:00
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-06-07 08:10:00 -04:00
# Date is set specifically in this scope to improve query time.
scope :expired_and_not_notified , - > { where ( [ " date(expires_at AT TIME ZONE 'UTC') BETWEEN '2000-01-01' AND CURRENT_DATE AND expiry_notification_delivered_at IS NULL " ] ) }
2021-04-07 20:09:11 -04:00
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 ] ) }
2019-12-17 04:07:48 -05:00
2018-10-30 12:48:06 -04:00
def self . regular_keys
where ( type : [ 'Key' , nil ] )
end
2016-11-15 14:16:45 -05:00
def key = ( value )
2018-02-15 09:50:19 -05:00
write_attribute ( :key , value . present? ? Gitlab :: SSHPublicKey . sanitize ( value ) : nil )
2017-10-03 13:31:16 -04:00
@public_key = nil
2012-02-07 17:32:20 -05:00
end
2015-06-19 13:17:34 -04:00
def publishable_key
2016-08-02 01:56:23 -04:00
# 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-06-19 13:17:34 -04:00
end
2012-06-07 08:44:57 -04:00
# projects that has this key
2011-10-08 17:36:38 -04:00
def projects
2013-05-06 05:26:36 -04:00
user . authorized_projects
2011-10-08 17:36:38 -04:00
end
2012-08-28 17:04:06 -04:00
2013-02-05 04:12:15 -05:00
def shell_id
2013-05-06 05:26:36 -04:00
" key- #{ id } "
2013-02-04 08:07:56 -05:00
end
2013-06-24 13:07:21 -04:00
2019-05-29 09:47:40 -04:00
# EE overrides this
def can_delete?
true
end
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ServiceClass
2016-12-21 09:59:54 -05:00
def update_last_used_at
2017-09-20 08:21:15 -04:00
Keys :: LastUsedService . new ( self ) . execute
2016-12-21 09:59:54 -05:00
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ServiceClass
2016-12-21 09:59:54 -05:00
2020-03-12 08:09:17 -04:00
def add_to_authorized_keys
return unless Gitlab :: CurrentSettings . authorized_keys_enabled?
AuthorizedKeysWorker . perform_async ( :add_key , shell_id , key )
2014-04-02 14:13:05 -04:00
end
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ServiceClass
2014-08-18 10:46:46 -04:00
def post_create_hook
SystemHooksService . new . execute_hooks_for ( self , :create )
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ServiceClass
2014-08-18 10:46:46 -04:00
2020-03-12 08:09:17 -04:00
def remove_from_authorized_keys
return unless Gitlab :: CurrentSettings . authorized_keys_enabled?
AuthorizedKeysWorker . perform_async ( :remove_key , shell_id )
2014-04-02 14:13:05 -04:00
end
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ServiceClass
2017-11-15 09:47:10 -05:00
def refresh_user_cache
return unless user
Users :: KeysCountService . new ( user ) . refresh_cache
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ServiceClass
2017-11-15 09:47:10 -05:00
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ServiceClass
2014-08-18 10:46:46 -04:00
def post_destroy_hook
SystemHooksService . new . execute_hooks_for ( self , :destroy )
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ServiceClass
2014-08-18 10:46:46 -04:00
2017-08-21 06:30:03 -04:00
def public_key
@public_key || = Gitlab :: SSHPublicKey . new ( key )
end
2013-06-24 13:07:21 -04:00
private
2015-01-18 10:29:37 -05:00
def generate_fingerprint
2013-07-17 09:16:34 -04:00
self . fingerprint = nil
2019-12-11 13:08:10 -05:00
self . fingerprint_sha256 = nil
2015-04-14 06:00:43 -04:00
2018-02-15 09:50:19 -05:00
return unless public_key . valid?
2015-04-14 06:00:43 -04:00
2019-12-11 13:08:10 -05:00
self . fingerprint_md5 = public_key . fingerprint
self . fingerprint_sha256 = public_key . fingerprint ( " SHA256 " ) . gsub ( " SHA256: " , " " )
2017-08-21 06:30:03 -04:00
end
2017-08-25 09:08:48 -04:00
def key_meets_restrictions
2018-02-02 13:39:55 -05:00
restriction = Gitlab :: CurrentSettings . key_restriction_for ( public_key . type )
2017-08-25 09:08:48 -04:00
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 " )
2017-08-21 06:30:03 -04:00
end
end
2017-08-25 09:08:48 -04:00
def forbidden_key_type_message
2020-01-07 19:07:43 -05:00
allowed_types = Gitlab :: CurrentSettings . allowed_key_types . map ( & :upcase )
2017-08-21 06:30:03 -04:00
2020-01-07 19:07:43 -05:00
" type is forbidden. Must be #{ Gitlab :: Utils . to_exclusive_sentence ( allowed_types ) } "
2013-06-24 13:07:21 -04:00
end
2011-10-08 17:36:38 -04:00
end
2019-09-13 09:26:31 -04:00
2021-05-11 17:10:21 -04:00
Key . prepend_mod_with ( 'Key' )