summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/action/api_2fa_recovery.rb12
-rw-r--r--lib/action/git_lfs_authenticate.rb10
-rw-r--r--lib/actor.rb3
-rw-r--r--lib/actor/base.rb4
-rw-r--r--lib/actor/key.rb2
-rw-r--r--lib/actor/username.rb19
-rw-r--r--lib/gitlab_keys.rb26
-rw-r--r--lib/gitlab_net.rb17
-rw-r--r--lib/gitlab_shell.rb26
9 files changed, 81 insertions, 38 deletions
diff --git a/lib/action/api_2fa_recovery.rb b/lib/action/api_2fa_recovery.rb
index 5597ff0..ad8130f 100644
--- a/lib/action/api_2fa_recovery.rb
+++ b/lib/action/api_2fa_recovery.rb
@@ -3,8 +3,8 @@ require_relative '../gitlab_logger'
module Action
class API2FARecovery < Base
- def initialize(key)
- @key = key
+ def initialize(actor)
+ @actor = actor
end
def execute(_, _)
@@ -13,7 +13,7 @@ module Action
private
- attr_reader :key
+ attr_reader :actor
def continue?(question)
puts "#{question} (yes/no)"
@@ -34,10 +34,10 @@ module Action
return
end
- resp = api.two_factor_recovery_codes(key.key_id)
+ resp = api.two_factor_recovery_codes(self)
if resp['success']
codes = resp['recovery_codes'].join("\n")
- $logger.info('API 2FA recovery success', user: key.log_username)
+ $logger.info('API 2FA recovery success', user: actor.log_username)
puts "Your two-factor authentication recovery codes are:\n\n" \
"#{codes}\n\n" \
"During sign in, use one of the codes above when prompted for\n" \
@@ -45,7 +45,7 @@ module Action
"a new device so you do not lose access to your account again."
true
else
- $logger.info('API 2FA recovery error', user: key.log_username)
+ $logger.info('API 2FA recovery error', user: actor.log_username)
puts "An error occurred while trying to generate new recovery codes.\n" \
"#{resp['message']}"
end
diff --git a/lib/action/git_lfs_authenticate.rb b/lib/action/git_lfs_authenticate.rb
index 218c71e..d2e6d76 100644
--- a/lib/action/git_lfs_authenticate.rb
+++ b/lib/action/git_lfs_authenticate.rb
@@ -3,15 +3,15 @@ require_relative '../gitlab_logger'
module Action
class GitLFSAuthenticate < Base
- def initialize(key, repo_name)
- @key = key
+ def initialize(actor, repo_name)
+ @actor = actor
@repo_name = repo_name
end
def execute(_, _)
GitlabMetrics.measure('lfs-authenticate') do
- $logger.info('Processing LFS authentication', user: key.log_username)
- lfs_access = api.lfs_authenticate(key.key_id, repo_name)
+ $logger.info('Processing LFS authentication', user: actor.log_username)
+ lfs_access = api.lfs_authenticate(self, repo_name)
return unless lfs_access
puts lfs_access.authentication_payload
@@ -21,6 +21,6 @@ module Action
private
- attr_reader :key, :repo_name
+ attr_reader :actor, :repo_name
end
end
diff --git a/lib/actor.rb b/lib/actor.rb
index eab1bc4..4e8b3b8 100644
--- a/lib/actor.rb
+++ b/lib/actor.rb
@@ -1,6 +1,7 @@
require_relative 'actor/base'
require_relative 'actor/key'
require_relative 'actor/user'
+require_relative 'actor/username'
module Actor
class UnsupportedActorError < StandardError; end
@@ -11,6 +12,8 @@ module Actor
Key.from(str, audit_usernames: audit_usernames)
when User.id_regex
User.from(str, audit_usernames: audit_usernames)
+ when Username.id_regex
+ Username.from(str, audit_usernames: audit_usernames)
else
raise UnsupportedActorError
end
diff --git a/lib/actor/base.rb b/lib/actor/base.rb
index 76cc522..319873f 100644
--- a/lib/actor/base.rb
+++ b/lib/actor/base.rb
@@ -31,6 +31,10 @@ module Actor
"#{self.class.identifier_prefix}-#{id}"
end
+ def identifier_key
+ self.class.identifier_key
+ end
+
def log_username
audit_usernames? ? username : "#{klass_name.downcase} with identifier #{identifier}"
end
diff --git a/lib/actor/key.rb b/lib/actor/key.rb
index 411ef15..46f013a 100644
--- a/lib/actor/key.rb
+++ b/lib/actor/key.rb
@@ -21,7 +21,7 @@ module Actor
def username
@username ||= begin
- user = GitlabNet.new.discover(key_id)
+ user = GitlabNet.new.discover(self)
user ? "@#{user['username']}" : ANONYMOUS_USER
end
end
diff --git a/lib/actor/username.rb b/lib/actor/username.rb
new file mode 100644
index 0000000..e0a07dd
--- /dev/null
+++ b/lib/actor/username.rb
@@ -0,0 +1,19 @@
+require_relative 'base'
+
+module Actor
+ class Username < Base
+ alias username identifier
+
+ def self.identifier_prefix
+ 'username'.freeze
+ end
+
+ def self.identifier_key
+ 'username'.freeze
+ end
+
+ def self.id_regex
+ /\Ausername\-\d+\Z/
+ end
+ end
+end
diff --git a/lib/gitlab_keys.rb b/lib/gitlab_keys.rb
index 30444c3..3ee2882 100644
--- a/lib/gitlab_keys.rb
+++ b/lib/gitlab_keys.rb
@@ -9,12 +9,20 @@ class GitlabKeys # rubocop:disable Metrics/ClassLength
attr_accessor :auth_file, :key
- def self.command(key_id)
+ def self.command(whatever)
+ "#{ROOT_PATH}/bin/gitlab-shell #{whatever}"
+ end
+
+ def self.command_key(key_id)
unless /\A[a-z0-9-]+\z/ =~ key_id
raise KeyError, "Invalid key_id: #{key_id.inspect}"
end
- "#{ROOT_PATH}/bin/gitlab-shell #{key_id}"
+ command(key_id)
+ end
+
+ def self.whatever_line(command, trailer)
+ "command=\"#{command}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{trailer}"
end
def self.key_line(key_id, public_key)
@@ -24,7 +32,17 @@ class GitlabKeys # rubocop:disable Metrics/ClassLength
raise KeyError, "Invalid public_key: #{public_key.inspect}"
end
- "command=\"#{command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}"
+ whatever_line(command_key(key_id), public_key)
+ end
+
+ def self.principal_line(username_key_id, principal)
+ principal.chomp!
+
+ if principal.include?("\n")
+ raise KeyError, "Invalid principal: #{principal.inspect}"
+ end
+
+ whatever_line(command_key(username_key_id), principal)
end
def initialize
@@ -119,7 +137,7 @@ class GitlabKeys # rubocop:disable Metrics/ClassLength
$logger.info('Removing key', key_id: @key_id)
open_auth_file('r+') do |f|
while line = f.gets # rubocop:disable Style/AssignmentInCondition
- next unless line.start_with?("command=\"#{self.class.command(@key_id)}\"")
+ next unless line.start_with?("command=\"#{self.class.command_key(@key_id)}\"")
f.seek(-line.length, IO::SEEK_CUR)
# Overwrite the line with #'s. Because the 'line' variable contains
# a terminating '\n', we write line.length - 1 '#' characters.
diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb
index 34e2ff6..0a3e383 100644
--- a/lib/gitlab_net.rb
+++ b/lib/gitlab_net.rb
@@ -26,22 +26,24 @@ class GitlabNet
env: env
}
- params[actor.class.identifier_key.to_sym] = actor.id
+ params[actor.identifier_key.to_sym] = actor.id
resp = post("#{internal_api_endpoint}/allowed", params)
determine_action(actor, resp)
end
- def discover(key_id)
- resp = get("#{internal_api_endpoint}/discover?key_id=#{key_id}")
+ def discover(actor)
+ resp = get("#{internal_api_endpoint}/discover?#{actor.identifier_key}=#{actor.id}")
JSON.parse(resp.body)
rescue JSON::ParserError, ApiUnreachableError
nil
end
- def lfs_authenticate(key_id, repo)
- params = { project: sanitize_path(repo), key_id: key_id }
+ def lfs_authenticate(actor, repo)
+ params = { project: sanitize_path(repo) }
+ params[actor.identifier_key.to_sym] = actor.id
+
resp = post("#{internal_api_endpoint}/lfs_authenticate", params)
GitlabLfsAuthentication.build_from_json(resp.body) if resp.code == HTTP_SUCCESS
@@ -75,8 +77,9 @@ class GitlabNet
nil
end
- def two_factor_recovery_codes(key_id)
- resp = post("#{internal_api_endpoint}/two_factor_recovery_codes", key_id: key_id)
+ def two_factor_recovery_codes(actor)
+ params = { actor.identifier_key.to_sym => actor.id }
+ resp = post("#{internal_api_endpoint}/two_factor_recovery_codes", params)
JSON.parse(resp.body) if resp.code == HTTP_SUCCESS
rescue
{}
diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb
index 68d39bc..7b2b2b1 100644
--- a/lib/gitlab_shell.rb
+++ b/lib/gitlab_shell.rb
@@ -3,7 +3,7 @@ require 'pathname'
require_relative 'gitlab_net'
require_relative 'gitlab_metrics'
-require_relative 'actor/key'
+require_relative 'actor'
class GitlabShell
API_2FA_RECOVERY_CODES_COMMAND = '2fa_recovery_codes'.freeze
@@ -18,9 +18,9 @@ class GitlabShell
Struct.new('ParsedCommand', :command, :git_access_command, :repo_name, :args)
- def initialize(key_str)
- @key_str = key_str
+ def initialize(who)
@config = GitlabConfig.new
+ @actor = Actor.new_from(who, audit_usernames: @config.audit_usernames)
end
# The origin_cmd variable contains UNTRUSTED input. If the user ran
@@ -28,22 +28,22 @@ class GitlabShell
# 'evil command'.
def exec(origin_cmd)
if !origin_cmd || origin_cmd.empty?
- puts "Welcome to GitLab, #{key.username}!"
+ puts "Welcome to GitLab, #{actor.username}!"
return true
end
parsed_command = parse_cmd(origin_cmd)
- action = determine_action(parsed_command)
+ action = determine_action(parsed_command) # FIXME: watch out
action.execute(parsed_command.command, parsed_command.args)
rescue GitlabNet::ApiUnreachableError
$stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable"
false
rescue AccessDeniedError, UnknownError => ex
- $logger.warn('Access denied', command: origin_cmd, user: key.log_username)
+ $logger.warn('Access denied', command: origin_cmd, user: actor.log_username)
$stderr.puts "GitLab: #{ex.message}"
false
rescue DisallowedCommandError
- $logger.warn('Denied disallowed command', command: origin_cmd, user: key.log_username)
+ $logger.warn('Denied disallowed command', command: origin_cmd, user: actor.log_username)
$stderr.puts 'GitLab: Disallowed command'
false
rescue InvalidRepositoryPathError
@@ -53,11 +53,7 @@ class GitlabShell
private
- attr_reader :config, :key_str
-
- def key
- @key ||= Actor::Key.from(key_str, audit_usernames: config.audit_usernames)
- end
+ attr_reader :config, :actor
def parse_cmd(cmd)
args = Shellwords.shellwords(cmd)
@@ -99,7 +95,7 @@ class GitlabShell
end
def determine_action(parsed_command)
- return Action::API2FARecovery.new(key) if parsed_command.command == API_2FA_RECOVERY_CODES_COMMAND
+ return Action::API2FARecovery.new(actor) if parsed_command.command == API_2FA_RECOVERY_CODES_COMMAND
GitlabMetrics.measure('verify-access') do
# GitlatNet#check_access will raise exception in the event of a problem
@@ -107,13 +103,13 @@ class GitlabShell
parsed_command.git_access_command,
nil,
parsed_command.repo_name,
- key,
+ actor,
'_any'
)
case parsed_command.command
when GIT_LFS_AUTHENTICATE_COMMAND
- Action::GitLFSAuthenticate.new(key, parsed_command.repo_name)
+ Action::GitLFSAuthenticate.new(actor, parsed_command.repo_name)
else
initial_action
end