diff options
author | Ash McKenzie <ash@the-rebellion.net> | 2018-07-20 17:11:49 +1000 |
---|---|---|
committer | Ash McKenzie <ash@the-rebellion.net> | 2018-07-24 13:41:06 +1000 |
commit | 85c33cec0a0ab192fc013d41663b21def11a27ae (patch) | |
tree | a63190d5775b2d48c07f6598be74f3bdc4795802 | |
parent | ba0b55feb3aad87d0343e5e39a8b106825cdda44 (diff) | |
download | gitlab-shell-85c33cec0a0ab192fc013d41663b21def11a27ae.tar.gz |
Add support for utilising CustomActionStatus
-rw-r--r-- | lib/gitlab_net.rb | 5 | ||||
-rw-r--r-- | lib/gitlab_shell.rb | 54 | ||||
-rw-r--r-- | lib/http_helper.rb | 5 | ||||
-rw-r--r-- | spec/gitlab_net_spec.rb | 25 | ||||
-rw-r--r-- | spec/gitlab_shell_spec.rb | 35 | ||||
-rw-r--r-- | spec/vcr_cassettes/allowed-custom-action.yml | 46 | ||||
-rw-r--r-- | spec/vcr_cassettes/allowed-pull.yml | 6 | ||||
-rw-r--r-- | spec/vcr_cassettes/allowed-push.yml | 6 | ||||
-rw-r--r-- | spec/vcr_cassettes/not-allowed-pull.yml | 46 |
9 files changed, 192 insertions, 36 deletions
diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index 8e3e66e..34850cf 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -2,12 +2,11 @@ require 'net/http' require 'openssl' require 'json' -require_relative 'gitlab_config' require_relative 'gitlab_logger' require_relative 'gitlab_access' require_relative 'gitlab_lfs_authentication' -require_relative 'httpunix' require_relative 'http_helper' +require_relative 'custom_action_status' class GitlabNet include HTTPHelper @@ -37,6 +36,8 @@ class GitlabNet case resp.code when HTTP_SUCCESS GitAccessStatus.create_from_json(resp.body) + when HTTP_MULTIPLE_CHOICES + CustomActionStatus.create_from_json(resp.body) else GitAccessStatus.new(false, 'API is not accessible', gl_repository: nil, diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index 9644cf4..74af62a 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -17,6 +17,7 @@ class GitlabShell # rubocop:disable Metrics/ClassLength }.freeze API_COMMANDS = %w(2fa_recovery_codes).freeze GL_PROTOCOL = 'ssh'.freeze + HTTP_SUCCESS_CODES = %w{200 201}.freeze attr_accessor :key_id, :gl_repository, :repo_name, :command, :git_access attr_reader :repo_path @@ -38,29 +39,29 @@ class GitlabShell # rubocop:disable Metrics/ClassLength args = Shellwords.shellwords(origin_cmd) args = parse_cmd(args) + access_result = nil if GIT_COMMANDS.include?(args.first) - GitlabMetrics.measure('verify-access') { verify_access } + GitlabMetrics.measure('verify-access') { access_result = verify_access } end - process_cmd(args) - - true + process_cmd(access_result, args) rescue GitlabNet::ApiUnreachableError $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable" false rescue AccessDeniedError => ex $logger.warn('Access denied', command: origin_cmd, user: log_username) - $stderr.puts "GitLab: #{ex.message}" false rescue DisallowedCommandError $logger.warn('Denied disallowed command', command: origin_cmd, user: log_username) - $stderr.puts "GitLab: Disallowed command" false rescue InvalidRepositoryPathError $stderr.puts "GitLab: Invalid repository path" false + rescue CustomActionStatus::UnsuccessfulError + $stderr.puts "GitLab: A custom action error has occurred" + false end protected @@ -101,17 +102,13 @@ class GitlabShell # rubocop:disable Metrics/ClassLength end def verify_access - status = api.check_access(@git_access, nil, @repo_name, @key_id, '_any', GL_PROTOCOL) + result = api.check_access(@git_access, nil, @repo_name, @key_id, '_any', GL_PROTOCOL) + raise AccessDeniedError, result.message unless result.allowed? - raise AccessDeniedError, status.message unless status.allowed? - - self.repo_path = status.repository_path - @gl_repository = status.gl_repository - @gitaly = status.gitaly - @username = status.gl_username + result end - def process_cmd(args) + def process_cmd(access_result, args) return send("api_#{@command}") if API_COMMANDS.include?(@command) if @command == 'git-lfs-authenticate' @@ -119,9 +116,23 @@ class GitlabShell # rubocop:disable Metrics/ClassLength $logger.info('Processing LFS authentication', user: log_username) lfs_authenticate end - return + return true end + case access_result + when GitAccessStatus + exec_cmd_for_gitaly(access_result, args) + when CustomActionStatus + exec_custom_action(access_result) + end + end + + def exec_cmd_for_gitaly(access_result, args) + self.repo_path = access_result.repository_path + @gl_repository = access_result.gl_repository + @gitaly = access_result.gitaly + @username = access_result.gl_username + executable = @command args = [repo_path] @@ -147,6 +158,16 @@ class GitlabShell # rubocop:disable Metrics/ClassLength exec_cmd(executable, *args) end + def exec_custom_action(access_result) + resp = access_result.execute(@key_id) + + unless resp || HTTP_SUCCESS_CODES.include?(resp.code) + raise CustomActionStatus::UnsuccessfulError, resp.body + end + + true + end + # This method is not covered by Rspec because it ends the current Ruby process. def exec_cmd(*args) # If you want to call a command without arguments, use @@ -183,7 +204,7 @@ class GitlabShell # rubocop:disable Metrics/ClassLength end def api - GitlabNet.new + GitlabNet.new # TODO: Can this be memoised? end def user @@ -213,7 +234,6 @@ class GitlabShell # rubocop:disable Metrics/ClassLength def lfs_authenticate lfs_access = api.lfs_authenticate(@key_id, @repo_name) - return unless lfs_access puts lfs_access.authentication_payload diff --git a/lib/http_helper.rb b/lib/http_helper.rb index 863a0ed..0396ef3 100644 --- a/lib/http_helper.rb +++ b/lib/http_helper.rb @@ -1,3 +1,6 @@ +require_relative 'gitlab_config' +require_relative 'httpunix' + module HTTPHelper READ_TIMEOUT = 300 HTTP_SUCCESS = '200'.freeze @@ -115,7 +118,7 @@ module HTTPHelper end def secret_token - @secret_token ||= File.read config.secret_file + @secret_token ||= File.read(config.secret_file) end def read_timeout diff --git a/spec/gitlab_net_spec.rb b/spec/gitlab_net_spec.rb index be2f4ba..92b5f82 100644 --- a/spec/gitlab_net_spec.rb +++ b/spec/gitlab_net_spec.rb @@ -266,13 +266,21 @@ describe GitlabNet, vcr: true do describe '#check_access' do context 'ssh key with access nil, to project' do - it 'should allow pull access for host' do - VCR.use_cassette("allowed-pull") do + it 'should allow push access for host' do + VCR.use_cassette("allowed-push") do access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh') + expect(access).to be_instance_of(GitAccessStatus) access.allowed?.should be_truthy end end + it 'should allow a custom action to be created' do + VCR.use_cassette("allowed-custom-action") do + access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh') + expect(access).to be_instance_of(CustomActionStatus) + end + end + it 'adds the secret_token to the request' do VCR.use_cassette("allowed-pull") do Net::HTTP::Post.any_instance.should_receive(:set_form_data).with(hash_including(secret_token: secret)) @@ -280,12 +288,21 @@ describe GitlabNet, vcr: true do end end - it 'should allow push access for host' do - VCR.use_cassette("allowed-push") do + it 'should allow pull access for host' do + VCR.use_cassette("allowed-pull") do access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh') + expect(access).to be_instance_of(GitAccessStatus) access.allowed?.should be_truthy end end + + it 'should not allow pull access for host' do + VCR.use_cassette("not-allowed-pull") do + access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh') + expect(access).to be_instance_of(GitAccessStatus) + access.allowed?.should be_falsey + end + end end context 'ssh access has been disabled' do diff --git a/spec/gitlab_shell_spec.rb b/spec/gitlab_shell_spec.rb index af84b29..b3279fd 100644 --- a/spec/gitlab_shell_spec.rb +++ b/spec/gitlab_shell_spec.rb @@ -1,6 +1,7 @@ require_relative 'spec_helper' require_relative '../lib/gitlab_shell' -require_relative '../lib/gitlab_access_status' +require_relative '../lib/git_access_status' +require_relative '../lib/custom_action_status' describe GitlabShell do before do @@ -153,7 +154,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } it "should process the command" do - subject.should_receive(:process_cmd).with(%W(git-upload-pack gitlab-ci.git)) + subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git)) end it "should execute the command" do @@ -188,7 +189,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } it "should process the command" do - subject.should_receive(:process_cmd).with(%W(git-upload-pack gitlab-ci.git)) + subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git)) end it "should execute the command" do @@ -212,7 +213,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } it "should process the command" do - subject.should_receive(:process_cmd).with(%W(git-receive-pack gitlab-ci.git)) + subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git)) end it "should execute the command" do @@ -234,7 +235,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } it "should process the command" do - subject.should_receive(:process_cmd).with(%W(git-receive-pack gitlab-ci.git)) + subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git)) end it "should execute the command" do @@ -253,6 +254,28 @@ describe GitlabShell do end end + context 'custom action' do + let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" } + let(:custom_action_access_result) { CustomActionStatus.create_from_json('{"status":true,"message":"Attempting to proxy to primary..","payload":{"api_endpoints":["fake/info_refs","fake/push"],"data":{"username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git"}}}') } + let(:http_success) { double(Net::HTTPOK, code: '200', body: 'OK') } + let(:http_error) { double(Net::HTTPInternalServerError, code: '500', body: 'Internal Server Error') } + + before { api.stub(check_access: custom_action_access_result) } + after { subject.exec(ssh_cmd) } + + it "should attempt execute the custom action" do + subject.should_receive(:exec_custom_action).with(custom_action_access_result) + end + + it "should execute the custom action" do + custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_success) + end + + it "should raise an exception when the custom action is unsuccessful" do + custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_error) + end + end + shared_examples_for 'upload-archive' do |command| let(:ssh_cmd) { "#{command} gitlab-ci.git" } let(:exec_cmd_params) { ['git-upload-archive', repo_path] } @@ -261,7 +284,7 @@ describe GitlabShell do after { subject.exec(ssh_cmd) } it "should process the command" do - subject.should_receive(:process_cmd).with(%W(git-upload-archive gitlab-ci.git)) + subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-archive gitlab-ci.git)) end it "should execute the command" do diff --git a/spec/vcr_cassettes/allowed-custom-action.yml b/spec/vcr_cassettes/allowed-custom-action.yml new file mode 100644 index 0000000..bfd16b7 --- /dev/null +++ b/spec/vcr_cassettes/allowed-custom-action.yml @@ -0,0 +1,46 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/internal/allowed + body: + encoding: US-ASCII + string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 300 + message: Multiple Choices + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Content-Length: + - '155' + Content-Type: + - application/json + Date: + - Wed, 21 Jun 2017 10:44:52 GMT + Etag: + - W/"45654cae433b5a9c5fbba1d45d382e52" + Vary: + - Origin + X-Frame-Options: + - SAMEORIGIN + X-Request-Id: + - 8d4b8b06-fb6e-4f94-832f-72f8e0afad5f + X-Runtime: + - '0.289759' + body: + encoding: UTF-8 + string: '{"status":true,"message":"Attempting to proxy to primary..","payload":{"api_endpoints":["fake/info_refs","fake/push"],"data":{"username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git"}}}' + http_version: + recorded_at: Wed, 21 Jun 2017 10:44:52 GMT +recorded_with: VCR 2.4.0 diff --git a/spec/vcr_cassettes/allowed-pull.yml b/spec/vcr_cassettes/allowed-pull.yml index 1636fd5..b073476 100644 --- a/spec/vcr_cassettes/allowed-pull.yml +++ b/spec/vcr_cassettes/allowed-pull.yml @@ -5,7 +5,7 @@ http_interactions: uri: http://localhost:3000/api/v4/internal/allowed body: encoding: US-ASCII - string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A + string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 @@ -35,9 +35,9 @@ http_interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - 8d4b8b06-fb6e-4f94-832f-72f8e0afad5f + - 67ab4954-19e6-42ce-aae6-55c8ae5a365e X-Runtime: - - '0.289759' + - '0.230871' body: encoding: UTF-8 string: '{"status":true,"gl_repository":"project-3","repository_path":"/Users/dzaporozhets/Projects/gitlab-development-kit/repositories/gitlab-org/gitlab-test.git"}' diff --git a/spec/vcr_cassettes/allowed-push.yml b/spec/vcr_cassettes/allowed-push.yml index b073476..1636fd5 100644 --- a/spec/vcr_cassettes/allowed-push.yml +++ b/spec/vcr_cassettes/allowed-push.yml @@ -5,7 +5,7 @@ http_interactions: uri: http://localhost:3000/api/v4/internal/allowed body: encoding: US-ASCII - string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A + string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 @@ -35,9 +35,9 @@ http_interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - 67ab4954-19e6-42ce-aae6-55c8ae5a365e + - 8d4b8b06-fb6e-4f94-832f-72f8e0afad5f X-Runtime: - - '0.230871' + - '0.289759' body: encoding: UTF-8 string: '{"status":true,"gl_repository":"project-3","repository_path":"/Users/dzaporozhets/Projects/gitlab-development-kit/repositories/gitlab-org/gitlab-test.git"}' diff --git a/spec/vcr_cassettes/not-allowed-pull.yml b/spec/vcr_cassettes/not-allowed-pull.yml new file mode 100644 index 0000000..8876010 --- /dev/null +++ b/spec/vcr_cassettes/not-allowed-pull.yml @@ -0,0 +1,46 @@ +--- +http_interactions: +- request: + method: post + uri: http://localhost:3000/api/v4/internal/allowed + body: + encoding: US-ASCII + string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 500 + message: Internal Server Error + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Content-Length: + - '155' + Content-Type: + - application/json + Date: + - Wed, 21 Jun 2017 10:44:52 GMT + Etag: + - W/"45654cae433b5a9c5fbba1d45d382e52" + Vary: + - Origin + X-Frame-Options: + - SAMEORIGIN + X-Request-Id: + - 67ab4954-19e6-42ce-aae6-55c8ae5a365e + X-Runtime: + - '0.230871' + body: + encoding: UTF-8 + string: '{"status":false,"gl_repository":"","repository_path":""}' + http_version: + recorded_at: Wed, 21 Jun 2017 10:44:52 GMT +recorded_with: VCR 2.4.0 |