2018-10-22 03:00:50 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-09-19 10:34:32 -04:00
|
|
|
module Gitlab
|
|
|
|
class LfsToken
|
2018-12-17 01:17:39 -05:00
|
|
|
module LfsTokenHelper
|
|
|
|
def user?
|
|
|
|
actor.is_a?(User)
|
|
|
|
end
|
|
|
|
|
|
|
|
def actor_name
|
|
|
|
user? ? actor.username : "lfs+deploy-key-#{actor.id}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include LfsTokenHelper
|
2016-09-19 10:34:32 -04:00
|
|
|
|
2020-05-28 23:08:11 -04:00
|
|
|
DEFAULT_EXPIRE_TIME = 7200 # Default value 2 hours
|
2018-12-17 01:17:39 -05:00
|
|
|
|
|
|
|
attr_accessor :actor
|
2018-12-05 02:03:28 -05:00
|
|
|
|
2016-09-19 10:34:32 -04:00
|
|
|
def initialize(actor)
|
|
|
|
@actor =
|
|
|
|
case actor
|
|
|
|
when DeployKey, User
|
|
|
|
actor
|
|
|
|
when Key
|
|
|
|
actor.user
|
|
|
|
else
|
|
|
|
raise 'Bad Actor'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-11 01:36:02 -05:00
|
|
|
def token
|
|
|
|
HMACToken.new(actor).token(DEFAULT_EXPIRE_TIME)
|
2018-12-17 01:17:39 -05:00
|
|
|
end
|
2016-09-19 10:34:32 -04:00
|
|
|
|
2019-09-27 20:06:20 -04:00
|
|
|
# When the token is an lfs one and the actor
|
|
|
|
# is blocked or the password has been changed,
|
|
|
|
# the token is no longer valid
|
2018-12-17 01:17:39 -05:00
|
|
|
def token_valid?(token_to_check)
|
2019-09-27 20:06:20 -04:00
|
|
|
HMACToken.new(actor).token_valid?(token_to_check) && valid_user?
|
2016-09-19 10:34:32 -04:00
|
|
|
end
|
|
|
|
|
2017-11-08 11:21:39 -05:00
|
|
|
def deploy_key_pushable?(project)
|
|
|
|
actor.is_a?(DeployKey) && actor.can_push_to?(project)
|
|
|
|
end
|
|
|
|
|
2016-09-19 10:34:32 -04:00
|
|
|
def type
|
2018-10-23 06:14:18 -04:00
|
|
|
user? ? :lfs_token : :lfs_deploy_token
|
2016-09-19 10:34:32 -04:00
|
|
|
end
|
|
|
|
|
2019-09-27 20:06:20 -04:00
|
|
|
def valid_user?
|
|
|
|
return true unless user?
|
|
|
|
|
2021-06-16 14:10:35 -04:00
|
|
|
!actor.blocked? && !actor.password_expired_if_applicable?
|
2019-09-27 20:06:20 -04:00
|
|
|
end
|
|
|
|
|
2019-02-10 23:05:00 -05:00
|
|
|
def authentication_payload(repository_http_path)
|
2019-02-10 22:34:10 -05:00
|
|
|
{
|
|
|
|
username: actor_name,
|
|
|
|
lfs_token: token,
|
2019-02-10 23:05:00 -05:00
|
|
|
repository_http_path: repository_http_path,
|
|
|
|
expires_in: DEFAULT_EXPIRE_TIME
|
2019-02-10 22:34:10 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2019-09-27 20:06:20 -04:00
|
|
|
def basic_encoding
|
|
|
|
ActionController::HttpAuthentication::Basic.encode_credentials(actor_name, token)
|
|
|
|
end
|
|
|
|
|
2019-01-16 07:09:29 -05:00
|
|
|
private # rubocop:disable Lint/UselessAccessModifier
|
2018-12-17 01:17:39 -05:00
|
|
|
|
|
|
|
class HMACToken
|
|
|
|
include LfsTokenHelper
|
|
|
|
|
|
|
|
def initialize(actor)
|
|
|
|
@actor = actor
|
|
|
|
end
|
|
|
|
|
|
|
|
def token(expire_time)
|
|
|
|
hmac_token = JSONWebToken::HMACToken.new(secret)
|
|
|
|
hmac_token.expire_time = Time.now + expire_time
|
|
|
|
hmac_token[:data] = { actor: actor_name }
|
|
|
|
hmac_token.encoded
|
|
|
|
end
|
|
|
|
|
|
|
|
def token_valid?(token_to_check)
|
|
|
|
decoded_token = JSONWebToken::HMACToken.decode(token_to_check, secret).first
|
|
|
|
decoded_token.dig('data', 'actor') == actor_name
|
|
|
|
rescue JWT::DecodeError
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :actor
|
|
|
|
|
|
|
|
def secret
|
|
|
|
salt + key
|
|
|
|
end
|
|
|
|
|
|
|
|
def salt
|
|
|
|
case actor
|
|
|
|
when DeployKey, Key
|
|
|
|
actor.fingerprint.delete(':').first(16)
|
|
|
|
when User
|
|
|
|
# Take the last 16 characters as they're more unique than the first 16
|
|
|
|
actor.id.to_s + actor.encrypted_password.last(16)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def key
|
|
|
|
# Take 16 characters of attr_encrypted_db_key_base, as that's what the
|
|
|
|
# cipher needs exactly
|
|
|
|
Settings.attr_encrypted_db_key_base.first(16)
|
|
|
|
end
|
2016-09-19 10:34:32 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|