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