Commit 89bd72d1 authored by Ash McKenzie's avatar Ash McKenzie

Merge branch '173-remove-unneeded-binaries' into 'master'

Remove dead Ruby code and unused binaries

See merge request gitlab-org/gitlab-shell!346
parents c88d80fe 8cf3f4c7
...@@ -4,7 +4,6 @@ tmp/* ...@@ -4,7 +4,6 @@ tmp/*
*.log *.log
/*.log* /*.log*
authorized_keys.lock authorized_keys.lock
coverage/
.gitlab_shell_secret .gitlab_shell_secret
.bundle .bundle
tags tags
...@@ -15,7 +14,4 @@ hooks/*.d ...@@ -15,7 +14,4 @@ hooks/*.d
/bin/gitlab-shell /bin/gitlab-shell
/bin/gitlab-shell-authorized-keys-check /bin/gitlab-shell-authorized-keys-check
/bin/gitlab-shell-authorized-principals-check /bin/gitlab-shell-authorized-principals-check
/bin/gitaly-upload-pack
/bin/gitaly-receive-pack
/bin/gitaly-upload-archive
/bin/check /bin/check
image: "ruby:2.3" .test: &test_definition
variables:
INSTALL_BUNDLER_VERSION: "~> 2.0.1"
before_script:
- export PATH=~/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
- gem install --force --bindir /usr/local/bin bundler -v "$INSTALL_BUNDLER_VERSION"
- cp config.yml.example config.yml
- bundle install
.rspec_definition: &rspec_definition
script:
# Skip the experimental Golang wrapper in the Ruby specs. These are now
# primarily regression tests for particular versions of Ruby.
#
# The full rspec suite is also run against each suppported golang version
- make test_ruby
rspec:
<<: *rspec_definition
except:
- tags
verify_ruby:
script:
- make verify_ruby
except:
- tags
#ruby 2.6
rspec:ruby2.6:
image: ruby:2.6
variables:
INSTALL_BUNDLER_VERSION: ~> 1.17.3
<<: *rspec_definition
except:
- tags
#ruby 2.2
rspec:ruby2.2:
image: ruby:2.2
variables:
INSTALL_BUNDLER_VERSION: ~> 1.17.3
<<: *rspec_definition
except:
- tags
#ruby 2.1
rspec:ruby2.1:
image: ruby:2.1
variables:
INSTALL_BUNDLER_VERSION: ~> 1.17.3
<<: *rspec_definition
except:
- tags
.go: &go_definition
before_script: before_script:
# Set up the environment to run integration tests (still written in Ruby)
- apt-get update -qq && apt-get install -y ruby ruby-dev - apt-get update -qq && apt-get install -y ruby ruby-dev
- ruby -v - ruby -v
- export PATH=~/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin - export PATH=~/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
- gem update --system - gem install --force --bindir /usr/local/bin bundler -v 1.17.2
- gem install --bindir /usr/local/bin bundler
- cp config.yml.example config.yml
- bundle install - bundle install
script: # Now set up to run the Golang tests
- make build
- cp config.yml.example config.yml
- go version - go version
- which go - which go
- make build verify_golang test_golang script:
# Run the full Ruby test suite in the "go" tests. As more functionality is - make verify test
# migrated into these tests and out of Ruby, the amount of work here will
# reduce
- make test_ruby
go:1.11: go:1.11:
<<: *go_definition <<: *test_definition
image: golang:1.11 image: golang:1.11
go:1.12: go:1.12:
<<: *go_definition <<: *test_definition
image: golang:1.12 image: golang:1.12
go:1.13: go:1.13:
<<: *go_definition <<: *test_definition
image: golang:1.13 image: golang:1.13
codequality: codequality:
......
source 'https://rubygems.org' source 'https://rubygems.org'
group :development, :test do group :development, :test do
gem 'listen', '~> 0.5.0'
gem 'pry', '~> 0.12.2'
gem 'rspec', '~> 3.8.0' gem 'rspec', '~> 3.8.0'
gem 'rspec-parameterized', '~> 0.4.0'
gem 'rubocop', '0.49.1', require: false gem 'rubocop', '0.49.1', require: false
gem 'simplecov', '~> 0.9.0', require: false
gem 'vcr', '~> 4.0.0'
gem 'webmock', '~> 3.4.0'
end end
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
abstract_type (0.0.7)
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
ast (2.4.0) ast (2.4.0)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
coderay (1.1.2)
concord (0.1.5)
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
crack (0.4.3)
safe_yaml (~> 1.0.0)
debug_inspector (0.0.3)
diff-lcs (1.3) diff-lcs (1.3)
docile (1.1.5)
equalizer (0.0.11)
hashdiff (0.3.7)
ice_nine (0.11.2)
listen (0.5.3)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.9.2)
multi_json (1.13.1)
parallel (1.12.1) parallel (1.12.1)
parser (2.5.1.2) parser (2.5.1.2)
ast (~> 2.4.0) ast (~> 2.4.0)
powerpack (0.1.2) powerpack (0.1.2)
proc_to_ast (0.1.0)
coderay
parser
unparser
procto (0.0.3)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (3.0.3)
rainbow (2.2.2) rainbow (2.2.2)
rake rake
rake (12.3.3) rake (12.3.3)
...@@ -55,12 +22,6 @@ GEM ...@@ -55,12 +22,6 @@ GEM
rspec-mocks (3.8.0) rspec-mocks (3.8.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0) rspec-support (~> 3.8.0)
rspec-parameterized (0.4.0)
binding_of_caller
parser
proc_to_ast
rspec (>= 2.13, < 4)
unparser
rspec-support (3.8.0) rspec-support (3.8.0)
rubocop (0.49.1) rubocop (0.49.1)
parallel (~> 1.10) parallel (~> 1.10)
...@@ -70,40 +31,14 @@ GEM ...@@ -70,40 +31,14 @@ GEM
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0) ruby-progressbar (1.9.0)
safe_yaml (1.0.4)
simplecov (0.9.2)
docile (~> 1.1.0)
multi_json (~> 1.0)
simplecov-html (~> 0.9.0)
simplecov-html (0.9.0)
thread_safe (0.3.6)
unicode-display_width (1.4.0) unicode-display_width (1.4.0)
unparser (0.2.8)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
concord (~> 0.1.5)
diff-lcs (~> 1.3)
equalizer (~> 0.0.9)
parser (>= 2.3.1.2, < 2.6)
procto (~> 0.0.2)
vcr (4.0.0)
webmock (3.4.2)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
PLATFORMS PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
listen (~> 0.5.0)
pry (~> 0.12.2)
rspec (~> 3.8.0) rspec (~> 3.8.0)
rspec-parameterized (~> 0.4.0)
rubocop (= 0.49.1) rubocop (= 0.49.1)
simplecov (~> 0.9.0)
vcr (~> 4.0.0)
webmock (~> 3.4.0)
BUNDLED WITH BUNDLED WITH
1.16.3 1.17.2
...@@ -14,15 +14,9 @@ verify_golang: ...@@ -14,15 +14,9 @@ verify_golang:
test: test_ruby test_golang test: test_ruby test_golang
# The Ruby tests are now all integration specs that test the Go implementation.
test_ruby: test_ruby:
# bin/gitlab-shell, bin/gitlab-shell-authorized-keys-check and bundle exec rspec --color --format d spec
# bin/gitlab-shell-authorized-principals-check must exist and need to be
# the Ruby version for rspec to be able to test.
cp bin/gitlab-shell-ruby bin/gitlab-shell
cp bin/gitlab-shell-authorized-keys-check-ruby bin/gitlab-shell-authorized-keys-check
cp bin/gitlab-shell-authorized-principals-check-ruby bin/gitlab-shell-authorized-principals-check
bundle exec rspec --color --tag '~go' --format d spec
rm -f bin/gitlab-shell bin/gitlab-shell-authorized-keys-check bin/gitlab-shell-authorized-principals-check
test_golang: test_golang:
support/go-test support/go-test
......
...@@ -45,26 +45,10 @@ plan](https://gitlab.com/gitlab-org/gitaly/issues/1226#note_126519133). ...@@ -45,26 +45,10 @@ plan](https://gitlab.com/gitlab-org/gitaly/issues/1226#note_126519133).
## Requirements ## Requirements
**GitLab shell will always use your system ruby (normally located at /usr/bin/ruby) and will not use the ruby your installed with a ruby version manager (such as RVM).** GitLab Shell is written in Go, and needs a Go compiler to build. It still requires
It requires ruby 2.0 or higher. Ruby to build and test, but not to run.
Please uninstall any old ruby versions from your system:
``` Download and install the current version of Go from https://golang.org/dl/
sudo apt-get remove ruby1.8
```
Download Ruby and compile it with:
```
mkdir /tmp/ruby && cd /tmp/ruby
curl -L --progress http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.5.tar.gz | tar xz
cd ruby-2.1.5
./configure --disable-install-rdoc
make
sudo make install
```
To install gitlab-shell you also need a Go compiler version 1.8 or newer. https://golang.org/dl/
## Setup ## Setup
...@@ -78,59 +62,25 @@ Checks if GitLab API access and redis via internal API can be reached: ...@@ -78,59 +62,25 @@ Checks if GitLab API access and redis via internal API can be reached:
## Testing ## Testing
Run Ruby and Golang tests: Run tests:
bundle install
make test make test
Run Rubocop and gofmt: Run gofmt and rubocop:
bundle install
make verify make verify
Run both test and verify (the default Makefile target): Run both test and verify (the default Makefile target):
bundle install
make validate make validate
## Git LFS remark ## Git LFS remark
Starting with GitLab 8.12, GitLab supports Git LFS authentication through SSH. Starting with GitLab 8.12, GitLab supports Git LFS authentication through SSH.
## Migration to Go feature flags
We are starting to migrate some features from Ruby to Go. To be able to do this
incrementally, we hide the Go implementation behind a feature flag.
To enable a feature, modify `migration` option in `config.yml` and ensure `enabled`
is set to `true` and feature to be enabled is added to `features`.
It should look something like this:
```yaml
migration:
enabled: true
features: ['discover']
```
Here are the following features that can be enabled:
- `discover`
- `2fa_recovery_codes`
### Configuring using Omnibus
If you're using Omnibus, these features can be enabled by adding something like this to `gitlab.rb`:
```ruby
gitlab_shell['migration'] = { enabled: true, features: ['discover', '2fa_recovery_codes'] }
```
This is equivalent to having this in `config.yml`:
```yaml
migration:
enabled: true
features: ['discover', '2fa_recovery_codes']
```
## Releasing a new version ## Releasing a new version
GitLab Shell is versioned by git tags, and the version used by the Rails GitLab Shell is versioned by git tags, and the version used by the Rails
...@@ -152,18 +102,6 @@ Rails application: ...@@ -152,18 +102,6 @@ Rails application:
version**. (Note: this can be done as a separate MR to that, or in and MR version**. (Note: this can be done as a separate MR to that, or in and MR
that will make use of the latest GitLab Shell changes.) that will make use of the latest GitLab Shell changes.)
## Updating VCR fixtures
In order to generate new VCR fixtures you need to have a local GitLab instance
running and have next data:
1. gitlab-org/gitlab-test project.
2. SSH key with access to the project and ID 1 that belongs to Administrator.
3. SSH key without access to the project and ID 2.
You also need to modify `secret` variable at `spec/gitlab_net_spec.rb` so tests
can connect to your local instance.
## Contributing ## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md). See [CONTRIBUTING.md](./CONTRIBUTING.md).
......
#!/usr/bin/env ruby
#
# GitLab shell authorized_keys helper. Query GitLab API to get the authorized
# command for a given ssh key fingerprint
#
# Ex.
# bin/gitlab-shell-authorized-keys-check <username> <public-key>
#
# Returns
# command="/bin/gitlab-shell key-#",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAA...
#
# Expects to be called by the SSH daemon, via configuration like:
# AuthorizedKeysCommandUser git
# AuthorizedKeysCommand /bin/gitlab-shell-authorized-keys-check git %u %k
abort "# Wrong number of arguments. #{ARGV.size}. Usage:
# gitlab-shell-authorized-keys-check <expected-username> <actual-username> <key>" unless ARGV.size == 3
expected_username = ARGV[0]
abort '# No username provided' if expected_username.nil? || expected_username == ''
actual_username = ARGV[1]
abort '# No username provided' if actual_username.nil? || actual_username == ''
# Only check access if the requested username matches the configured username.
# Normally, these would both be 'git', but it can be configured by the user
exit 0 unless expected_username == actual_username
key = ARGV[2]
abort "# No key provided" if key.nil? || key == ''
require_relative '../lib/gitlab_init'
require_relative '../lib/gitlab_net'
require_relative '../lib/gitlab_keys'
authorized_key = GitlabNet.new.authorized_key(key)
if authorized_key.nil?
puts "# No key was found for #{key}"
else
puts GitlabKeys.key_line("key-#{authorized_key['id']}", authorized_key['key'])
end
#!/usr/bin/env ruby
#
# GitLab shell authorized principals helper. Emits the same sort of
# command="..." line as gitlab-shell-authorized-principals-check, with
# the right options.
#
# Ex.
# bin/gitlab-shell-authorized-keys-check <key-id> <principal1> [<principal2>...]
#
# Returns one line per principal passed in, e.g.:
# command="/bin/gitlab-shell username-{KEY_ID}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty {PRINCIPAL}
# [command="/bin/gitlab-shell username-{KEY_ID}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty {PRINCIPAL2}]
#
# Expects to be called by the SSH daemon, via configuration like:
# AuthorizedPrincipalsCommandUser root
# AuthorizedPrincipalsCommand /bin/gitlab-shell-authorized-principals-check git %i sshUsers
abort "# Wrong number of arguments. #{ARGV.size}. Usage:
# gitlab-shell-authorized-principals-check <key-id> <principal1> [<principal2>...]" unless ARGV.size >= 2
key_id = ARGV[0]
abort '# No key_id provided' if key_id.nil? || key_id == ''
principals = ARGV[1..-1]
principals.each { |principal|
abort '# An invalid principal was provided' if principal.nil? || principal == ''
}
require_relative '../lib/gitlab_init'
require_relative '../lib/gitlab_net'
require_relative '../lib/gitlab_keys'
principals.each { |principal|
puts GitlabKeys.principal_line("username-#{key_id}", principal.dup)
}
#!/usr/bin/env ruby
unless ENV['SSH_CONNECTION']
puts "Only ssh allowed"
exit
end
original_cmd = ENV.delete('SSH_ORIGINAL_COMMAND')
require_relative '../lib/gitlab_init'
#
#
# GitLab shell, invoked from ~/.ssh/authorized_keys or from an
# AuthorizedPrincipalsCommand in the key-less SSH CERT mode.
#
#
require File.join(ROOT_PATH, 'lib', 'gitlab_shell')
# We must match e.g. "key-12345" anywhere on the command-line. See
# https://gitlab.com/gitlab-org/gitlab-shell/issues/145
who = /\b(?:(?:key)-[0-9]+|username-\S+)\b/.match(ARGV.join(' ')).to_s
if GitlabShell.new(who).exec(original_cmd)
exit 0
else
exit 1
end
#!/usr/bin/env ruby
# The purpose of this executable is to test that gitlab-shell logging
# works correctly in combination with Kernel.exec.
require_relative '../lib/gitlab_init'
require_relative '../lib/gitlab_logger'
$logger.info(ARGV.first)
Kernel.exec('true')
package main
import (
"context"
"encoding/json"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/handler"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/logger"
"google.golang.org/grpc"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func init() {
logger.ProgName = "gitaly-receive-pack"
}
func main() {
handler.RunGitalyCommand(func(ctx context.Context, conn *grpc.ClientConn, requestJSON string) (int32, error) {
request, err := deserialize(requestJSON)
if err != nil {
return 1, err
}
return handler.ReceivePack(ctx, conn, request)
})
}
func deserialize(requestJSON string) (*pb.SSHReceivePackRequest, error) {
var request pb.SSHReceivePackRequest
if err := json.Unmarshal([]byte(requestJSON), &request); err != nil {
return nil, err
}
return &request, nil
}
package main
import (
"testing"
"github.com/stretchr/testify/require"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func Test_deserialize(t *testing.T) {
tests := []struct {
name string
requestJSON string
want *pb.SSHReceivePackRequest
wantErr bool
}{
{
name: "empty",
requestJSON: "",
want: nil,
wantErr: true,
},
{
name: "empty_hash",
requestJSON: "{}",
want: &pb.SSHReceivePackRequest{},
wantErr: false,
},
{
name: "nil",
requestJSON: "null",
want: &pb.SSHReceivePackRequest{},
wantErr: false,
},
{
name: "values",
requestJSON: `{"gl_id": "1234"}`,
want: &pb.SSHReceivePackRequest{GlId: "1234"},
wantErr: false,
},
{
name: "invalid_json",
requestJSON: `{"gl_id": "1234`,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := deserialize(tt.requestJSON)
require.EqualValues(t, got, tt.want, "Got %+v, wanted %+v", got, tt.want)
if tt.wantErr {
require.Error(t, err, "Wanted an error, got %+v", err)
} else {
require.NoError(t, err, "Wanted no error, got %+v", err)
}
})
}
}
package main
import (
"context"
"encoding/json"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/handler"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/logger"
"google.golang.org/grpc"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func init() {
logger.ProgName = "gitaly-upload-archive"
}
func main() {
handler.RunGitalyCommand(func(ctx context.Context, conn *grpc.ClientConn, requestJSON string) (int32, error) {
request, err := deserialize(requestJSON)
if err != nil {
return 1, err
}
return handler.UploadArchive(ctx, conn, request)
})
}
func deserialize(argumentJSON string) (*pb.SSHUploadArchiveRequest, error) {
var request pb.SSHUploadArchiveRequest
if err := json.Unmarshal([]byte(argumentJSON), &request); err != nil {
return nil, err
}
return &request, nil
}
package main
import (
"testing"
"github.com/stretchr/testify/require"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func Test_deserialize(t *testing.T) {
tests := []struct {
name string
requestJSON string
want *pb.SSHUploadArchiveRequest
wantErr bool
}{
{
name: "empty",
requestJSON: "",
want: nil,
wantErr: true,
},
{
name: "empty_hash",
requestJSON: "{}",
want: &pb.SSHUploadArchiveRequest{},
wantErr: false,
},
{
name: "nil",
requestJSON: "null",
want: &pb.SSHUploadArchiveRequest{},
wantErr: false,
},
{
name: "values",
requestJSON: `{"repository": { "storage_name": "12345"} }`,
want: &pb.SSHUploadArchiveRequest{Repository: &pb.Repository{StorageName: "12345"}},
wantErr: false,
},
{
name: "invalid_json",
requestJSON: `{"gl_id": "1234`,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := deserialize(tt.requestJSON)
require.EqualValues(t, got, tt.want, "Got %+v, wanted %+v", got, tt.want)
if tt.wantErr {
require.Error(t, err, "Wanted an error, got %+v", err)
} else {
require.NoError(t, err, "Wanted no error, got %+v", err)
}
})
}
}
package main
import (
"context"
"encoding/json"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/handler"
"gitlab.com/gitlab-org/gitlab-shell/go/internal/logger"
"google.golang.org/grpc"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func init() {
logger.ProgName = "gitaly-upload-pack"
}
func main() {
handler.RunGitalyCommand(func(ctx context.Context, conn *grpc.ClientConn, requestJSON string) (int32, error) {
request, err := deserialize(requestJSON)
if err != nil {
return 1, err
}
return handler.UploadPack(ctx, conn, request)
})
}
func deserialize(requestJSON string) (*pb.SSHUploadPackRequest, error) {
var request pb.SSHUploadPackRequest
if err := json.Unmarshal([]byte(requestJSON), &request); err != nil {
return nil, err
}
return &request, nil
}
package main
import (
"testing"
"github.com/stretchr/testify/require"
pb "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
)
func Test_deserialize(t *testing.T) {
tests := []struct {
name string
requestJSON string
want *pb.SSHUploadPackRequest
wantErr bool
}{
{
name: "empty",
requestJSON: "",
want: nil,
wantErr: true,
},
{
name: "empty_hash",
requestJSON: "{}",
want: &pb.SSHUploadPackRequest{},
wantErr: false,
},
{
name: "nil",
requestJSON: "null",
want: &pb.SSHUploadPackRequest{},
wantErr: false,
},
{
name: "values",
requestJSON: `{"repository": { "storage_name": "12345"} }`,
want: &pb.SSHUploadPackRequest{Repository: &pb.Repository{StorageName: "12345"}},
wantErr: false,
},
{
name: "invalid_json",
requestJSON: `{"gl_id": "1234`,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := deserialize(tt.requestJSON)
require.EqualValues(t, got, tt.want, "Got %+v, wanted %+v", got, tt.want)
if tt.wantErr {
require.Error(t, err, "Wanted an error, got %+v", err)
} else {
require.NoError(t, err, "Wanted no error, got %+v", err)
}
})
}
}
require_relative 'action/custom'
module Action
end
require 'base64'
require_relative '../http_helper'
require_relative '../console_helper'
module Action
class Custom
include HTTPHelper
include ConsoleHelper
class BaseError < StandardError; end
class MissingPayloadError < BaseError; end
class MissingAPIEndpointsError < BaseError; end
class MissingDataError < BaseError; end
class UnsuccessfulError < BaseError; end
NO_MESSAGE_TEXT = 'No message'.freeze
DEFAULT_HEADERS = { 'Content-Type' => CONTENT_TYPE_JSON }.freeze
def initialize(gl_id, payload)
@gl_id = gl_id
@payload = payload
end
def execute
validate!
inform_client(info_message) if info_message
process_api_endpoints!
end
private
attr_reader :gl_id, :payload
def process_api_endpoints!
output = ''
resp = nil
data_with_gl_id = data.merge('gl_id' => gl_id)
api_endpoints.each do |endpoint|
url = "#{base_url}#{endpoint}"
json = { 'data' => data_with_gl_id, 'output' => output }
resp = post(url, {}, headers: DEFAULT_HEADERS, options: { json: json })
# Net::HTTPSuccess is the parent of Net::HTTPOK, Net::HTTPCreated etc.
case resp
when Net::HTTPSuccess, Net::HTTPMultipleChoices
true
else
raise_unsuccessful!(resp)
end
begin
body = JSON.parse(resp.body)
rescue JSON::ParserError
raise UnsuccessfulError, 'Response was not valid JSON'
end
print_flush(body['result'])
# In the context of the git push sequence of events, it's necessary to read
# stdin in order to capture output to pass onto subsequent commands
output = read_stdin
end
resp
end
def base_url
config.gitlab_url
end
def data
@data ||= payload['data']
end
def api_endpoints
data['api_endpoints']
end
def info_message
data['info_message']
end
def config
@config ||= GitlabConfig.new
end
def api
@api ||= GitlabNet.new
end
def read_stdin
Base64.encode64($stdin.read)
end
def print_flush(str)
return false unless str
$stdout.print(Base64.decode64(str))
$stdout.flush
end
def inform_client(str)
$stderr.puts(format_gitlab_output(str))
end
def format_gitlab_output(str)
format_for_stderr(str.split("\n")).join("\n")
end
def validate!
validate_payload!
validate_data!
validate_api_endpoints!
end
def validate_payload!
raise MissingPayloadError if !payload.is_a?(Hash) || payload.empty?
end
def validate_data!
raise MissingDataError unless data.is_a?(Hash)
end
def validate_api_endpoints!
raise MissingAPIEndpointsError if !api_endpoints.is_a?(Array) ||
api_endpoints.empty?
end
def raise_unsuccessful!(result)
message = "#{exception_message_for(result.body)} (#{result.code})"
raise UnsuccessfulError, format_gitlab_output(message)
end
def exception_message_for(body)
body = JSON.parse(body)
return body['message'] unless body['message'].to_s.empty?
body['result'].to_s.empty? ? NO_MESSAGE_TEXT : Base64.decode64(body['result'])
rescue JSON::ParserError
NO_MESSAGE_TEXT
end
end
end
# frozen_string_literal: true
module ConsoleHelper
LINE_PREFACE = '> GitLab:'
def write_stderr(messages)
format_for_stderr(messages).each do |message|
$stderr.puts(message)
end
end
def format_for_stderr(messages)
Array(messages).each_with_object([]) do |message, all|
all << "#{LINE_PREFACE} #{message}" unless message.empty?
end
end
end
require 'json'
class GitAccessStatus
HTTP_MULTIPLE_CHOICES = '300'.freeze
attr_reader :message, :gl_repository, :gl_project_path, :gl_id, :gl_username,
:gitaly, :git_protocol, :git_config_options, :payload,
:gl_console_messages
def initialize(status, status_code, message, gl_repository: nil,
gl_project_path: nil, gl_id: nil,
gl_username: nil, gitaly: nil, git_protocol: nil,
git_config_options: nil, payload: nil, gl_console_messages: [])
@status = status
@status_code = status_code
@message = message
@gl_repository = gl_repository
@gl_project_path = gl_project_path
@gl_id = gl_id
@gl_username = gl_username
@git_config_options = git_config_options
@gitaly = gitaly
@git_protocol = git_protocol
@payload = payload
@gl_console_messages = gl_console_messages
end
def self.create_from_json(json, status_code)
values = JSON.parse(json)
new(values["status"],
status_code,
values["message"],
gl_repository: values["gl_repository"],
gl_project_path: values["gl_project_path"],
gl_id: values["gl_id"],
gl_username: values["gl_username"],
git_config_options: values["git_config_options"],
gitaly: values["gitaly"],
git_protocol: values["git_protocol"],
payload: values["payload"],
gl_console_messages: values["gl_console_messages"])
end
def allowed?
@status
end
def custom_action?
@status_code == HTTP_MULTIPLE_CHOICES
end
end
require 'yaml'
class GitlabConfig
attr_reader :config
def initialize
@config = YAML.load_file(File.join(ROOT_PATH, 'config.yml'))
end
def home
ENV['HOME']
end
def auth_file
@config['auth_file'] ||= File.join(home, ".ssh/authorized_keys")
end
def secret_file
@config['secret_file'] ||= File.join(ROOT_PATH, '.gitlab_shell_secret')
end
# Pass a default value because this is called from a repo's context; in which
# case, the repo's hooks directory should be the default.
#
def custom_hooks_dir(default: nil)
@config['custom_hooks_dir'] || default
end
def gitlab_url
(@config['gitlab_url'] ||= "http://localhost:8080").sub(%r{/*$}, '')
end
def http_settings
@config['http_settings'] ||= {}
end
def log_file
@config['log_file'] ||= File.join(ROOT_PATH, 'gitlab-shell.log')
end
def log_level
@config['log_level'] ||= 'INFO'
end
def log_format
@config['log_format'] ||= 'text'
end
def audit_usernames
@config['audit_usernames'] ||= false
end
def metrics_log_file
@config['metrics_log_file'] ||= File.join(ROOT_PATH, 'gitlab-shell-metrics.log')
end
end
ROOT_PATH = ENV.fetch('GITLAB_SHELL_DIR', File.expand_path('..', __dir__))
# We are transitioning parts of gitlab-shell into the gitaly project. In
# gitaly, GITALY_EMBEDDED will be true.
GITALY_EMBEDDED = false
require_relative 'gitlab_config'
module GitlabKeys
class KeyError < StandardError; end
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
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)
public_key.chomp!
if public_key.include?("\n")
raise KeyError, "Invalid public_key: #{public_key.inspect}"
end
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
end
require 'base64'
require 'json'
class GitlabLfsAuthentication
# TODO: These don't need to be public
attr_accessor :username, :lfs_token, :repository_http_path
def initialize(username, lfs_token, repository_http_path, expires_in = nil)
@username = username
@lfs_token = lfs_token
@repository_http_path = repository_http_path
@expires_in = expires_in
end
def self.build_from_json(json)
values = JSON.parse(json)
new(values['username'],
values['lfs_token'],
values['repository_http_path'],
values['expires_in'])
rescue
nil
end
# Source: https://github.com/git-lfs/git-lfs/blob/master/docs/api/server-discovery.md#ssh
#
def authentication_payload
payload = { header: { Authorization: authorization }, href: href }
payload[:expires_in] = @expires_in if @expires_in
JSON.generate(payload)
end
private
def authorization
"Basic #{Base64.strict_encode64("#{username}:#{lfs_token}")}"
end
def href
"#{repository_http_path}/info/lfs"
end
end
require 'json'
require 'logger'
require 'time'
require_relative 'gitlab_config'
def convert_log_level(log_level)
Logger.const_get(log_level.upcase)
rescue NameError
$stderr.puts "WARNING: Unrecognized log level #{log_level.inspect}."
$stderr.puts "WARNING: Falling back to INFO."
Logger::INFO
end
class GitlabLogger
# Emulate the quoting logic of logrus
# https://github.com/sirupsen/logrus/blob/v1.0.5/text_formatter.go#L143-L156
SHOULD_QUOTE = /[^a-zA-Z0-9\-._\/@^+]/
LEVELS = {
Logger::INFO => 'info'.freeze,
Logger::DEBUG => 'debug'.freeze,
Logger::WARN => 'warn'.freeze,
Logger::ERROR => 'error'.freeze
}.freeze
def initialize(level, path, log_format)
@level = level
@log_file = File.open(path, 'ab')
# By default Ruby will buffer writes. This is a problem when we exec
# into a new command before Ruby flushed its buffers. Setting 'sync' to
# true disables Ruby's buffering.
@log_file.sync = true
@log_format = log_format
end
def info(message, data = {})
log_at(Logger::INFO, message, data)
end
def debug(message, data = {})
log_at(Logger::DEBUG, message, data)
end
def warn(message, data = {})
log_at(Logger::WARN, message, data)
end
def error(message, data = {})
log_at(Logger::ERROR, message, data)
end
private
attr_reader :log_file, :log_format
def log_at(level, message, data)
return unless @level <= level
data[:pid] = pid
data[:level] = LEVELS[level]
data[:msg] = message
# Use RFC3339 to match logrus in the Go parts of gitlab-shell
data[:time] = time_now.to_datetime.rfc3339
case log_format
when 'json'
# Don't use IO#puts because of https://bugs.ruby-lang.org/issues/14042
log_file.print("#{format_json(data)}\n")
else
log_file.print("#{format_text(data)}\n")
end
end
def pid
Process.pid
end
def time_now
Time.now
end
def format_text(data)
# We start the line with these fields to match the behavior of logrus
result = [
format_key_value(:time, data.delete(:time)),
format_key_value(:level, data.delete(:level)),
format_key_value(:msg, data.delete(:msg))
]
data.sort.each { |k, v| result << format_key_value(k, v) }
result.join(' ')
end
def format_key_value(key, value)
value_string = value.to_s
value_string = value_string.inspect if SHOULD_QUOTE =~ value_string
"#{key}=#{value_string}"
end
def format_json(data)
data.each do |key, value|
next unless value.is_a?(String)
value = value.dup.force_encoding('utf-8')
value = value.inspect unless value.valid_encoding?
data[key] = value.freeze
end
data.to_json
end
end
config = GitlabConfig.new
$logger = GitlabLogger.new(convert_log_level(config.log_level), config.log_file, config.log_format)
require_relative 'gitlab_config'
require_relative 'gitlab_logger'
module GitlabMetrics
module System
# THREAD_CPUTIME is not supported on OS X
if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
def self.cpu_time
Process.
clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
end
else
def self.cpu_time
Process.
clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
end
end
# Returns the current monotonic clock time in a given precision.
#
# Returns the time as a Fixnum.
def self.monotonic_time
if defined?(Process::CLOCK_MONOTONIC)
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
else
Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
end
end
end
def self.logger
$logger
end
# Measures the execution time of a block.
#
# Example:
#
# GitlabMetrics.measure(:find_by_username_duration) do
# User.find_by_username(some_username)
# end
#
# name - The name of the field to store the execution time in.
#
# Returns the value yielded by the supplied block.
def self.measure(name)
start_real = System.monotonic_time
start_cpu = System.cpu_time
retval = yield
real_time = System.monotonic_time - start_real
cpu_time = System.cpu_time - start_cpu
logger.debug('metrics', name: name, wall_time: real_time, cpu_time: cpu_time)
retval
end
end
require 'net/http'
require 'openssl'
require 'json'
require_relative 'gitlab_config'
require_relative 'gitlab_access_status'
require_relative 'gitlab_lfs_authentication'
require_relative 'http_helper'
class GitlabNet # rubocop:disable Metrics/ClassLength
include HTTPHelper
CHECK_TIMEOUT = 5
API_INACCESSIBLE_MESSAGE = 'API is not accessible'.freeze
def check_access(cmd, gl_repository, repo, who, changes, protocol, env: {})
changes = changes.join("\n") unless changes.is_a?(String)
params = {
action: cmd,
changes: changes,
gl_repository: gl_repository,
project: sanitize_path(repo),
protocol: protocol,
env: env
}
who_sym, _, who_v = self.class.parse_who(who)
params[who_sym] = who_v
url = "#{internal_api_endpoint}/allowed"
resp = post(url, params)
case resp
when Net::HTTPSuccess, Net::HTTPMultipleChoices, Net::HTTPUnauthorized,
Net::HTTPNotFound, Net::HTTPServiceUnavailable
if resp.content_type == CONTENT_TYPE_JSON
return GitAccessStatus.create_from_json(resp.body, resp.code)
end
end
GitAccessStatus.new(false, resp.code, API_INACCESSIBLE_MESSAGE)
end
def discover(who)
_, who_k, who_v = self.class.parse_who(who)
resp = get("#{internal_api_endpoint}/discover?#{who_k}=#{who_v}")
JSON.parse(resp.body) rescue nil
end
def lfs_authenticate(gl_id, repo, operation)
id_sym, _, id = self.class.parse_who(gl_id)
params = { project: sanitize_path(repo), operation: operation }
case id_sym
when :key_id
params[:key_id] = id
when :user_id
params[:user_id] = id
else
raise ArgumentError, "lfs_authenticate() got unsupported GL_ID='#{gl_id}'!"
end
resp = post("#{internal_api_endpoint}/lfs_authenticate", params)
GitlabLfsAuthentication.build_from_json(resp.body) if resp.code == '200'
end
def broadcast_message
resp = get("#{internal_api_endpoint}/broadcast_message")
JSON.parse(resp.body) rescue {}
end
def merge_request_urls(gl_repository, repo_path, changes)
changes = changes.join("\n") unless changes.is_a?(String)
changes = changes.encode('UTF-8', 'ASCII', invalid: :replace, replace: '')
url = "#{internal_api_endpoint}/merge_request_urls?project=#{URI.escape(repo_path)}&changes=#{URI.escape(changes)}"
url += "&gl_repository=#{URI.escape(gl_repository)}" if gl_repository
resp = get(url)
if resp.code == '200'
JSON.parse(resp.body)
else
[]
end
rescue
[]
end
def check
get("#{internal_api_endpoint}/check", options: { read_timeout: CHECK_TIMEOUT })
end
def authorized_key(key)
resp = get("#{internal_api_endpoint}/authorized_keys?key=#{URI.escape(key, '+/=')}")
JSON.parse(resp.body) if resp.code == "200"
rescue
nil
end
def two_factor_recovery_codes(gl_id)
id_sym, _, id = self.class.parse_who(gl_id)
resp = post("#{internal_api_endpoint}/two_factor_recovery_codes", id_sym => id)
JSON.parse(resp.body) if resp.code == '200'
rescue
{}
end
def notify_post_receive(gl_repository, repo_path)
params = { gl_repository: gl_repository, project: repo_path }
resp = post("#{internal_api_endpoint}/notify_post_receive", params)
resp.code == '200'
rescue
false
end
def post_receive(gl_repository, identifier, changes, push_options)
params = {
gl_repository: gl_repository,
identifier: identifier,
changes: changes,
:"push_options[]" => push_options, # rubocop:disable Style/HashSyntax
}
resp = post("#{internal_api_endpoint}/post_receive", params)
raise NotFound if resp.code == '404'
JSON.parse(resp.body) if resp.code == '200'
end
def pre_receive(gl_repository)
resp = post("#{internal_api_endpoint}/pre_receive", gl_repository: gl_repository)
raise NotFound if resp.code == '404'
JSON.parse(resp.body) if resp.code == '200'
end
def self.parse_who(who)
if who.start_with?("key-")
value = who.gsub("key-", "")
raise ArgumentError, "who='#{who}' is invalid!" unless value =~ /\A[0-9]+\z/
[:key_id, 'key_id', value]
elsif who.start_with?("user-")
value = who.gsub("user-", "")
raise ArgumentError, "who='#{who}' is invalid!" unless value =~ /\A[0-9]+\z/
[:user_id, 'user_id', value]
elsif who.start_with?("username-")
[:username, 'username', who.gsub("username-", "")]
else
raise ArgumentError, "who='#{who}' is invalid!"
end
end
protected
def sanitize_path(repo)
repo.delete("'")
end
end
class GitlabNet
class ApiUnreachableError < StandardError; end
class NotFound < StandardError; end
end
# frozen_string_literal: true
require 'shellwords'
require 'pathname'
require_relative 'gitlab_net'
require_relative 'gitlab_metrics'
require_relative 'action'
require_relative 'console_helper'
class GitlabShell # rubocop:disable Metrics/ClassLength
include ConsoleHelper
class AccessDeniedError < StandardError; end
class DisallowedCommandError < StandardError; end
class InvalidRepositoryPathError < StandardError; end
GIT_UPLOAD_PACK_COMMAND = 'git-upload-pack'
GIT_RECEIVE_PACK_COMMAND = 'git-receive-pack'
GIT_UPLOAD_ARCHIVE_COMMAND = 'git-upload-archive'
GIT_LFS_AUTHENTICATE_COMMAND = 'git-lfs-authenticate'
GITALY_COMMANDS = {
GIT_UPLOAD_PACK_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-upload-pack'),
GIT_UPLOAD_ARCHIVE_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-upload-archive'),
GIT_RECEIVE_PACK_COMMAND => File.join(ROOT_PATH, 'bin', 'gitaly-receive-pack')
}.freeze
GIT_COMMANDS = (GITALY_COMMANDS.keys + [GIT_LFS_AUTHENTICATE_COMMAND]).freeze
TWO_FACTOR_RECOVERY_COMMAND = '2fa_recovery_codes'
GL_PROTOCOL = 'ssh'
attr_accessor :gl_id, :gl_repository, :gl_project_path, :repo_name, :command, :git_access, :git_protocol
def initialize(who)
who_sym, = GitlabNet.parse_who(who)
if who_sym == :username
@who = who
else
@gl_id = who
end
@config = GitlabConfig.new
end
# The origin_cmd variable contains UNTRUSTED input. If the user ran
# ssh git@gitlab.example.com 'evil command', then origin_cmd contains
# 'evil command'.
def exec(origin_cmd)
unless origin_cmd
puts "Welcome to GitLab, #{username}!"
return true
end
args = Shellwords.shellwords(origin_cmd)
args = parse_cmd(args)
access_status = nil
if GIT_COMMANDS.include?(args.first)
access_status = GitlabMetrics.measure('verify-access') { verify_access }
@gl_repository = access_status.gl_repository
@git_protocol = ENV['GIT_PROTOCOL']
@gl_project_path = access_status.gl_project_path
@gitaly = access_status.gitaly
@username = access_status.gl_username
@git_config_options = access_status.git_config_options
@gl_id = access_status.gl_id if defined?(@who)
write_stderr(access_status.gl_console_messages)
elsif !defined?(@gl_id)
# We're processing an API command like 2fa_recovery_codes, but
# don't have a @gl_id yet, that means we're in the "username"
# mode and need to materialize it, calling the "user" method
# will do that and call the /discover method.
user
end
if @command == GIT_RECEIVE_PACK_COMMAND && access_status.custom_action?
# If the response from /api/v4/allowed is a HTTP 300, we need to perform
# a Custom Action and therefore should return and not call process_cmd()
#
return process_custom_action(access_status)
end
process_cmd(args)
true
rescue GitlabNet::ApiUnreachableError
write_stderr('Failed to authorize your Git request: internal API unreachable')
false
rescue AccessDeniedError => ex
$logger.warn('Access denied', command: origin_cmd, user: log_username)
write_stderr(ex.message)
false
rescue DisallowedCommandError
$logger.warn('Denied disallowed command', command: origin_cmd, user: log_username)
write_stderr('Disallowed command')
false
rescue InvalidRepositoryPathError
write_stderr('Invalid repository path')
false
rescue Action::Custom::BaseError => ex
$logger.warn('Custom action error', exception: ex.class, message: ex.message,
command: origin_cmd, user: log_username)
$stderr.puts ex.message
false
end
protected
def parse_cmd(args)
# Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack
if args.length == 3 && args.first == 'git'
@command = "git-#{args[1]}"
args = [@command, args.last]
else
@command = args.first
end
@git_access = @command
return args if TWO_FACTOR_RECOVERY_COMMAND == @command
raise DisallowedCommandError unless GIT_COMMANDS.include?(@command)
case @command
when GIT_LFS_AUTHENTICATE_COMMAND
raise DisallowedCommandError unless args.count >= 2
@repo_name = args[1]
case args[2]
when 'download'
@git_access = GIT_UPLOAD_PACK_COMMAND
when 'upload'
@git_access = GIT_RECEIVE_PACK_COMMAND
else
raise DisallowedCommandError
end
else
raise DisallowedCommandError unless args.count == 2
@repo_name = args.last
end
args
end
def verify_access
status = api.check_access(@git_access, nil, @repo_name, @who || @gl_id, '_any', GL_PROTOCOL)
raise AccessDeniedError, status.message unless status.allowed?
status
end
def process_custom_action(access_status)
Action::Custom.new(@gl_id, access_status.payload).execute
end
def process_cmd(args)
return api_2fa_recovery_codes if TWO_FACTOR_RECOVERY_COMMAND == @command
if @command == GIT_LFS_AUTHENTICATE_COMMAND
GitlabMetrics.measure('lfs-authenticate') do
operation = args[2]
$logger.info('Processing LFS authentication', operation: operation, user: log_username)
lfs_authenticate(operation)
end
return
end
# TODO: instead of building from pieces here in gitlab-shell, build the
# entire gitaly_request in gitlab-ce and pass on as-is here.
args = JSON.dump(
'repository' => @gitaly['repository'],
'gl_repository' => @gl_repository,
'gl_project_path' => @gl_project_path,
'gl_id' => @gl_id,
'gl_username' => @username,
'git_config_options' => @git_config_options,
'git_protocol' => @git_protocol
)
gitaly_address = @gitaly['address']
executable = GITALY_COMMANDS.fetch(@command)
gitaly_bin = File.basename(executable)
args_string = [gitaly_bin, gitaly_address, args].join(' ')
$logger.info('executing git command', command: args_string, user: log_username)
exec_cmd(executable, gitaly_address: gitaly_address, token: @gitaly['token'], json_args: args)
end
# This method is not covered by Rspec because it ends the current Ruby process.
def exec_cmd(executable, gitaly_address:, token:, json_args:)
env = { 'GITALY_TOKEN' => token }
args = [executable, gitaly_address, json_args]
# We use 'chdir: ROOT_PATH' to let the next executable know where config.yml is.
Kernel.exec(env, *args, unsetenv_others: true, chdir: ROOT_PATH)
end
def api
GitlabNet.new
end
def user
return @user if defined?(@user)
begin
if defined?(@who)
@user = api.discover(@who)
@gl_id = "user-#{@user['id']}" if @user && @user.key?('id')
else
@user = api.discover(@gl_id)
end
rescue GitlabNet::ApiUnreachableError
@user = nil
end
end
def username_from_discover
return nil unless user && user['username']
"@#{user['username']}"
end
def username
@username ||= username_from_discover || 'Anonymous'
end
# User identifier to be used in log messages.
def log_username
@config.audit_usernames ? username : "user with id #{@gl_id}"
end
def lfs_authenticate(operation)
lfs_access = api.lfs_authenticate(@gl_id, @repo_name, operation)
return unless lfs_access
puts lfs_access.authentication_payload
end
private
def continue?(question)
puts "#{question} (yes/no)"
STDOUT.flush # Make sure the question gets output before we wait for input
continue = STDIN.gets.chomp
puts '' # Add a buffer in the output
continue == 'yes'
end
def api_2fa_recovery_codes
continue = continue?(
"Are you sure you want to generate new two-factor recovery codes?\n" \
"Any existing recovery codes you saved will be invalidated."
)
unless continue
puts 'New recovery codes have *not* been generated. Existing codes will remain valid.'
return
end
resp = api.two_factor_recovery_codes(@gl_id)
if resp['success']
codes = resp['recovery_codes'].join("\n")
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" \
"your two-factor code. Then, visit your Profile Settings and add\n" \
"a new device so you do not lose access to your account again."
else
puts "An error occurred while trying to generate new recovery codes.\n" \
"#{resp['message']}"
end
end
end
module HooksUtils
module_function
# Gets an array of Git push options from the environment
def get_push_options
count = ENV['GIT_PUSH_OPTION_COUNT'].to_i
result = []
count.times do |i|
result.push(ENV["GIT_PUSH_OPTION_#{i}"])
end
result
end
end
require_relative 'httpunix'
require_relative 'gitlab_logger'
require_relative 'gitlab_net/errors'
module HTTPHelper
READ_TIMEOUT = 300
CONTENT_TYPE_JSON = 'application/json'.freeze
protected
def config
@config ||= GitlabConfig.new
end
def base_api_endpoint
"#{config.gitlab_url}/api/v4"
end
def internal_api_endpoint
"#{base_api_endpoint}/internal"
end
def http_client_for(uri, options = {})
http = if uri.is_a?(URI::HTTPUNIX)
Net::HTTPUNIX.new(uri.hostname)
else
Net::HTTP.new(uri.host, uri.port)
end
http.read_timeout = options[:read_timeout] || read_timeout
if uri.is_a?(URI::HTTPS)
http.use_ssl = true
http.cert_store = cert_store
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.http_settings['self_signed_cert']
end
http
end
def http_request_for(method, uri, params: {}, headers: {}, options: {})
request_klass = method == :get ? Net::HTTP::Get : Net::HTTP::Post
request = request_klass.new(uri.request_uri, headers)
user = config.http_settings['user']
password = config.http_settings['password']
request.basic_auth(user, password) if user && password
if options[:json]
request.body = options[:json].merge(secret_token: secret_token).to_json
else
request.set_form_data(params.merge(secret_token: secret_token))
end
if uri.is_a?(URI::HTTPUNIX)
# The HTTPUNIX HTTP client does not set a correct Host header. This can
# lead to 400 Bad Request responses.
request['Host'] = 'localhost'
end
request
end
def request(method, url, params: {}, headers: {}, options: {})
$logger.debug('Performing request', method: method.to_s.upcase, url: url)
uri = URI.parse(url)
http = http_client_for(uri, options)
request = http_request_for(method, uri,
params: params,
headers: headers,
options: options)
begin
start_time = Time.new
response = http.start { http.request(request) }
rescue => e
$logger.warn('Failed to connect', method: method.to_s.upcase, url: url, error: e)
raise GitlabNet::ApiUnreachableError
ensure
fields = { method: method.to_s.upcase, url: url, duration: Time.new - start_time, gitaly_embedded: GITALY_EMBEDDED }
$logger.info('finished HTTP request', fields)
end
case response
when Net::HTTPSuccess, Net::HTTPMultipleChoices
$logger.debug('Received response', code: response.code, body: response.body)
else
$logger.error('Call failed', method: method.to_s.upcase, url: url, code: response.code, body: response.body)
end
response
end
def get(url, headers: {}, options: {})
request(:get, url, headers: headers, options: options)
end
def post(url, params, headers: {}, options: {})
request(:post, url, params: params, headers: headers, options: options)
end
def cert_store
@cert_store ||= begin
store = OpenSSL::X509::Store.new
store.set_default_paths
ca_file = config.http_settings['ca_file']
store.add_file(ca_file) if ca_file
ca_path = config.http_settings['ca_path']
store.add_path(ca_path) if ca_path
store
end
end
def secret_token
@secret_token ||= File.read config.secret_file
end
def read_timeout
config.http_settings['read_timeout'] || READ_TIMEOUT
end
end
# support for http+unix://... connection scheme
#
# The URI scheme has the same structure as the similar one for python requests. See:
# http://fixall.online/theres-no-need-to-reinvent-the-wheelhttpsgithubcommsabramorequests-unixsocketurl/241810/
# https://github.com/msabramo/requests-unixsocket
require 'uri'
require 'net/http'
module URI
class HTTPUNIX < HTTP
def hostname
# decode %XX from path to file
v = host
URI.decode(v)
end
# port is not allowed in URI
DEFAULT_PORT = nil
def set_port(v)
return v unless v
raise InvalidURIError, "http+unix:// cannot contain port"
end
end
@@schemes['HTTP+UNIX'] = HTTPUNIX
end
# Based on:
# - http://stackoverflow.com/questions/15637226/ruby-1-9-3-simple-get-request-to-unicorn-through-socket
# - Net::HTTP::connect
module Net
class HTTPUNIX < HTTP
def initialize(socketpath, port = nil)
super(socketpath, port)
@port = nil # HTTP will set it to default - override back -> set DEFAULT_PORT
end
# override to prevent ":<port>" being appended to HTTP_HOST
def addr_port
address
end
def connect
D "opening connection to #{address} ..."
s = UNIXSocket.new(address)
D "opened"
@socket = BufferedIO.new(s)
@socket.read_timeout = @read_timeout
@socket.continue_timeout = @continue_timeout
@socket.debug_output = @debug_output
on_connect
end
end
end
require 'pathname'
class ObjectDirsHelper
class << self
def all_attributes
{
"GIT_ALTERNATE_OBJECT_DIRECTORIES" => absolute_alt_object_dirs,
"GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE" => relative_alt_object_dirs,
"GIT_OBJECT_DIRECTORY" => absolute_object_dir,
"GIT_OBJECT_DIRECTORY_RELATIVE" => relative_object_dir
}
end
def absolute_object_dir
ENV['GIT_OBJECT_DIRECTORY']
end
def relative_object_dir
relative_path(absolute_object_dir)
end
def absolute_alt_object_dirs
ENV['GIT_ALTERNATE_OBJECT_DIRECTORIES'].to_s.split(File::PATH_SEPARATOR)
end
def relative_alt_object_dirs
absolute_alt_object_dirs.map { |dir| relative_path(dir) }.compact
end
private
def relative_path(absolute_path)
return if absolute_path.nil?
repo_dir = Dir.pwd
Pathname.new(absolute_path).relative_path_from(Pathname.new(repo_dir)).to_s
end
end
end
require_relative '../spec_helper'
require_relative '../../lib/action/custom'
describe Action::Custom do
let(:repo_name) { 'gitlab-ci.git' }
let(:gl_id) { 'key-1' }
let(:secret) { "0a3938d9d95d807e94d937af3a4fbbea" }
let(:base_url) { 'http://localhost:3000' }
subject { described_class.new(gl_id, payload) }
describe '#execute' do
context 'with an empty payload' do
let(:payload) { {} }
it 'raises a MissingPayloadError exception' do
expect { subject.execute }.to raise_error(Action::Custom::MissingPayloadError)
end
end
context 'with api_endpoints defined' do
before do
allow(subject).to receive(:base_url).and_return(base_url)
allow(subject).to receive(:secret_token).and_return(secret)
allow($stdin).to receive(:read).and_return('')
end
context 'that are valid' do
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => %w{/api/v4/fake/info_refs /api/v4/fake/push},
'primary_repo' => 'http://localhost:3001/user1/repo1.git'
}
}
end
context 'and responds correctly' do
it 'prints a Base64 encoded result to $stdout' do
VCR.use_cassette("custom-action-ok") do
expect($stdout).to receive(:print).with('info_refs-result').ordered
expect($stdout).to receive(:print).with('push-result').ordered
subject.execute
end
end
context 'with results printed to $stdout' do
before do
allow($stdout).to receive(:print).with('info_refs-result')
allow($stdout).to receive(:print).with('push-result')
end
it 'returns an instance of Net::HTTPCreated' do
VCR.use_cassette("custom-action-ok") do
expect(subject.execute).to be_instance_of(Net::HTTPCreated)
end
end
context 'and with an information message provided' do
before do
payload['data']['info_message'] = 'Important message here.'
end
it 'prints said informational message to $stderr' do
VCR.use_cassette("custom-action-ok-with-message") do
expect { subject.execute }.to output(/Important message here./).to_stderr
end
end
end
end
end
context 'but responds incorrectly' do
it 'raises an UnsuccessfulError exception' do
VCR.use_cassette("custom-action-ok-not-json") do
expect do
subject.execute
end.to raise_error(Action::Custom::UnsuccessfulError, 'Response was not valid JSON')
end
end
end
end
context 'that are invalid' do
context 'where api_endpoints gl_id is missing' do
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'primary_repo' => 'http://localhost:3001/user1/repo1.git'
}
}
end
it 'raises a MissingAPIEndpointsError exception' do
expect { subject.execute }.to raise_error(Action::Custom::MissingAPIEndpointsError)
end
end
context 'where api_endpoints are empty' do
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => [],
'primary_repo' => 'http://localhost:3001/user1/repo1.git'
}
}
end
it 'raises a MissingAPIEndpointsError exception' do
expect { subject.execute }.to raise_error(Action::Custom::MissingAPIEndpointsError)
end
end
context 'where data gl_id is missing' do
let(:payload) { { 'api_endpoints' => %w{/api/v4/fake/info_refs /api/v4/fake/push} } }
it 'raises a MissingDataError exception' do
expect { subject.execute }.to raise_error(Action::Custom::MissingDataError)
end
end
context 'where API endpoints are bad' do
let(:payload) do
{
'action' => 'geo_proxy_to_primary',
'data' => {
'api_endpoints' => %w{/api/v4/fake/info_refs_bad /api/v4/fake/push_bad},
'primary_repo' => 'http://localhost:3001/user1/repo1.git'
}
}
end
context 'and response is JSON' do
it 'raises an UnsuccessfulError exception' do
VCR.use_cassette("custom-action-not-ok-json") do
expect do
subject.execute
end.to raise_error(Action::Custom::UnsuccessfulError, '> GitLab: You cannot perform write operations on a read-only instance (403)')
end
end
end
context 'and response is not JSON' do
it 'raises an UnsuccessfulError exception' do
VCR.use_cassette("custom-action-not-ok-not-json") do
expect do
subject.execute
end.to raise_error(Action::Custom::UnsuccessfulError, '> GitLab: No message (403)')
end
end
end
end
end
end
end
end
require_relative 'spec_helper'
require_relative '../lib/console_helper'
describe ConsoleHelper do
using RSpec::Parameterized::TableSyntax
class DummyClass
include ConsoleHelper
end
subject { DummyClass.new }
describe '#write_stderr' do
where(:messages, :stderr_output) do
'test' | "> GitLab: test\n"
%w{test1 test2} | "> GitLab: test1\n> GitLab: test2\n"
end
with_them do
it 'puts to $stderr, prefaced with > GitLab:' do
expect { subject.write_stderr(messages) }.to output(stderr_output).to_stderr
end
end
end
describe '#format_for_stderr' do
where(:messages, :result) do
'test' | ['> GitLab: test']
%w{test1 test2} | ['> GitLab: test1', '> GitLab: test2']
end
with_them do
it 'returns message(s), prefaced with > GitLab:' do
expect(subject.format_for_stderr(messages)).to eq(result)
end
end
end
end
require_relative 'spec_helper'
require_relative '../lib/gitlab_config'
describe GitlabConfig do
let(:config) { GitlabConfig.new }
let(:config_data) { {} }
before { expect(YAML).to receive(:load_file).and_return(config_data) }
describe '#gitlab_url' do
let(:url) { 'http://test.com' }
subject { config.gitlab_url }
before { config_data['gitlab_url'] = url }
it { is_expected.not_to be_empty }
it { is_expected.to eq(url) }
context 'remove trailing slashes' do
before { config_data['gitlab_url'] = url + '//' }
it { is_expected.to eq(url) }
end
end
describe '#audit_usernames' do
subject { config.audit_usernames }
it("returns false by default") { is_expected.to eq(false) }
end
describe '#log_format' do
subject { config.log_format }
it 'returns "text" by default' do
is_expected.to eq('text')
end
end
end
require_relative 'spec_helper'
require_relative '../lib/gitlab_keys'
require 'stringio'
describe GitlabKeys do
before do
$logger = double('logger').as_null_object
end
describe '.command' do
it 'the internal "command" utility function' do
command = "#{ROOT_PATH}/bin/gitlab-shell does-not-validate"
expect(described_class.command('does-not-validate')).to eq(command)
end
it 'does not raise a KeyError on invalid input' do
command = "#{ROOT_PATH}/bin/gitlab-shell foo\nbar\nbaz\n"
expect(described_class.command("foo\nbar\nbaz\n")).to eq(command)
end
end
describe '.command_key' do
it 'returns the "command" part of the key line' do
command = "#{ROOT_PATH}/bin/gitlab-shell key-123"
expect(described_class.command_key('key-123')).to eq(command)
end
it 'raises KeyError on invalid input' do
expect { described_class.command_key("\nssh-rsa AAA") }.to raise_error(described_class::KeyError)
end
end
describe '.key_line' do
let(:line) { %(command="#{ROOT_PATH}/bin/gitlab-shell key-741",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaDAxx2E) }
it 'returns the key line' do
expect(described_class.key_line('key-741', 'ssh-rsa AAAAB3NzaDAxx2E')).to eq(line)
end
it 'silently removes a trailing newline' do
expect(described_class.key_line('key-741', "ssh-rsa AAAAB3NzaDAxx2E\n")).to eq(line)
end
it 'raises KeyError on invalid input' do
expect { described_class.key_line('key-741', "ssh-rsa AAA\nssh-rsa AAA") }.to raise_error(described_class::KeyError)
end
end
describe '.principal_line' do
let(:line) { %(command="#{ROOT_PATH}/bin/gitlab-shell username-someuser",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty sshUsers) }
it 'returns the key line' do
expect(described_class.principal_line('username-someuser', 'sshUsers')).to eq(line)
end
it 'silently removes a trailing newline' do
expect(described_class.principal_line('username-someuser', "sshUsers\n")).to eq(line)
end
it 'raises KeyError on invalid input' do
expect { described_class.principal_line('username-someuser', "sshUsers\nloginUsers") }.to raise_error(described_class::KeyError)
end
end
end
require 'spec_helper'
require 'gitlab_lfs_authentication'
require 'json'
describe GitlabLfsAuthentication do
let(:payload_from_gitlab_api) do
{
username: 'dzaporozhets',
lfs_token: 'wsnys8Zm8Jn7zyhHTAAK',
repository_http_path: 'http://gitlab.dev/repo'
}
end
subject do
GitlabLfsAuthentication.build_from_json(
JSON.generate(payload_from_gitlab_api)
)
end
describe '#build_from_json' do
it { expect(subject.username).to eq('dzaporozhets') }
it { expect(subject.lfs_token).to eq('wsnys8Zm8Jn7zyhHTAAK') }
it { expect(subject.repository_http_path).to eq('http://gitlab.dev/repo') }
end
describe '#authentication_payload' do
shared_examples 'a valid payload' do
it 'should be proper JSON' do
payload = subject.authentication_payload
json_payload = JSON.parse(payload)
expect(json_payload['header']['Authorization']).to eq('Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL')
expect(json_payload['href']).to eq('http://gitlab.dev/repo/info/lfs')
end
end
context 'without expires_in' do
let(:result) { { 'header' => { 'Authorization' => 'Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL' }, 'href' => 'http://gitlab.dev/repo/info/lfs' }.to_json }
it { expect(subject.authentication_payload).to eq(result) }
it_behaves_like 'a valid payload'
end
context 'with expires_in' do
let(:result) { { 'header' => { 'Authorization' => 'Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL' }, 'href' => 'http://gitlab.dev/repo/info/lfs', 'expires_in' => 1800 }.to_json }
before do
payload_from_gitlab_api[:expires_in] = 1800
end
it { expect(subject.authentication_payload).to eq(result) }
it_behaves_like 'a valid payload'
end
end
end
require_relative 'spec_helper'
require_relative '../lib/gitlab_logger'
require 'securerandom'
describe :convert_log_level do
subject { convert_log_level :extreme }
it "converts invalid log level to Logger::INFO" do
expect($stderr).to receive(:puts).at_least(:once)
is_expected.to eq(Logger::INFO)
end
end
describe GitlabLogger do
subject { described_class.new(level, '/dev/null', format) }
let(:format) { 'text' }
let(:output) { StringIO.new }
let(:level) { Logger::INFO }
let(:time) { Time.at(123_456_789).utc } # '1973-11-29T21:33:09+00:00'
let(:pid) { 1234 }
before do
allow(subject).to receive(:log_file).and_return(output)
allow(subject).to receive(:time_now).and_return(time)
allow(subject).to receive(:pid).and_return(pid)
end
def first_line
output.string.lines.first.chomp
end
describe 'field sorting' do
it 'sorts fields, except time, level, msg' do
# Intentionally put 'foo' before 'baz' to see the effect of sorting
subject.info('hello world', foo: 'bar', baz: 'qux')
expect(first_line).to eq('time="1973-11-29T21:33:09+00:00" level=info msg="hello world" baz=qux foo=bar pid=1234')
end
end
describe '#error' do
context 'when the log level is too high' do
let(:level) { Logger::FATAL }
it 'does nothing' do
subject.info('hello world')
expect(output.string).to eq('')
end
end
it 'logs data' do
subject.error('hello world', foo: 'bar')
expect(first_line).to eq('time="1973-11-29T21:33:09+00:00" level=error msg="hello world" foo=bar pid=1234')
end
end
describe '#info' do
context 'when the log level is too high' do
let(:level) { Logger::ERROR }
it 'does nothing' do
subject.info('hello world')
expect(output.string).to eq('')
end
end
it 'logs data' do
subject.info('hello world', foo: 'bar')
expect(first_line).to eq('time="1973-11-29T21:33:09+00:00" level=info msg="hello world" foo=bar pid=1234')
end
end
describe '#warn' do
context 'when the log level is too high' do
let(:level) { Logger::ERROR }
it 'does nothing' do
subject.warn('hello world')
expect(output.string).to eq('')
end
end
it 'logs data' do
subject.warn('hello world', foo: 'bar')
expect(first_line).to eq('time="1973-11-29T21:33:09+00:00" level=warn msg="hello world" foo=bar pid=1234')
end
end
describe '#debug' do
it 'does nothing' do
subject.debug('hello world')
expect(output.string).to eq('')
end
context 'when the log level is low enough' do
let(:level) { Logger::DEBUG }
it 'logs data' do
subject.debug('hello world', foo: 'bar')
expect(first_line).to eq('time="1973-11-29T21:33:09+00:00" level=debug msg="hello world" foo=bar pid=1234')
end
end
end
describe 'json logging' do
let(:format) { 'json' }
it 'writes valid JSON data' do
subject.info('hello world', foo: 'bar')
expect(JSON.parse(first_line)).to eq(
'foo' => 'bar',
'level' => 'info',
'msg' => 'hello world',
'pid' => 1234,
'time' => '1973-11-29T21:33:09+00:00'
)
end
it 'handles non-UTF8 string values' do
subject.info("hello\x80world")
expect(JSON.parse(first_line)).to include('msg' => '"hello\x80world"')
end
end
describe 'log flushing' do
it 'logs get written even when calling Kernel.exec' do
msg = SecureRandom.hex(12)
test_logger_status = system('bin/test-logger', msg)
expect(test_logger_status).to eq(true)
grep_status = system('grep', '-q', '-e', msg, GitlabConfig.new.log_file)
expect(grep_status).to eq(true)
end
end
end
require_relative 'spec_helper'
require_relative '../lib/gitlab_metrics'
describe GitlabMetrics do
describe '.measure' do
before do
$logger = double('logger').as_null_object
end
it 'returns the return value of the block' do
val = described_class.measure('foo') { 10 }
expect(val).to eq(10)
end
it 'writes the metrics data to a log file' do
expect($logger).to receive(:debug).
with('metrics', a_metrics_log_message('foo'))
described_class.measure('foo') { 10 }
end
it 'calls proper measure methods' do
expect(described_class::System).to receive(:monotonic_time).twice.and_call_original
expect(described_class::System).to receive(:cpu_time).twice.and_call_original
described_class.measure('foo') { 10 }
end
end
end
RSpec::Matchers.define :a_metrics_log_message do |x|
match do |actual|
[
actual.fetch(:name) == x,
actual.fetch(:wall_time).is_a?(Numeric),
actual.fetch(:cpu_time).is_a?(Numeric),
].all?
end
end
require_relative 'spec_helper'
require_relative '../lib/gitlab_net'
require_relative '../lib/gitlab_access_status'
describe GitlabNet, vcr: true do
using RSpec::Parameterized::TableSyntax
let(:gitlab_net) { described_class.new }
let(:changes) { ['0000000000000000000000000000000000000000 92d0970eefd7acb6d548878925ce2208cfe2d2ec refs/heads/branch4'] }
let(:base_api_endpoint) { 'http://localhost:3000/api/v4' }
let(:internal_api_endpoint) { 'http://localhost:3000/api/v4/internal' }
let(:project) { 'gitlab-org/gitlab-test.git' }
let(:key) { 'key-1' }
let(:key2) { 'key-2' }
let(:secret) { "0a3938d9d95d807e94d937af3a4fbbea\n" }
before do
$logger = double('logger').as_null_object
allow(gitlab_net).to receive(:base_api_endpoint).and_return(base_api_endpoint)
allow(gitlab_net).to receive(:secret_token).and_return(secret)
end
describe '#check' do
it 'should return 200 code for gitlab check' do
VCR.use_cassette("check-ok") do
result = gitlab_net.check
expect(result.code).to eq('200')
end
end
it 'adds the secret_token to request' do
VCR.use_cassette("check-ok") do
expect_any_instance_of(Net::HTTP::Get).to receive(:set_form_data).with(hash_including(secret_token: secret))
gitlab_net.check
end
end
it "raises an exception if the connection fails" do
allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError)
expect { gitlab_net.check }.to raise_error(GitlabNet::ApiUnreachableError)
end
end
describe '#discover' do
it 'should return user has based on key id' do
VCR.use_cassette("discover-ok") do
user = gitlab_net.discover(key)
expect(user['name']).to eq('Administrator')
expect(user['username']).to eq('root')
end
end
it 'adds the secret_token to request' do
VCR.use_cassette("discover-ok") do
expect_any_instance_of(Net::HTTP::Get).to receive(:set_form_data).with(hash_including(secret_token: secret))
gitlab_net.discover(key)
end
end
it "raises an exception if the connection fails" do
VCR.use_cassette("discover-ok") do
allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError)
expect { gitlab_net.discover(key) }.to raise_error(GitlabNet::ApiUnreachableError)
end
end
end
describe '#lfs_authenticate' do
context 'lfs authentication succeeded' do
let(:repository_http_path) { URI.join(internal_api_endpoint.sub('/api/v4', ''), project).to_s }
context 'for download operation' do
it 'should return the correct data' do
VCR.use_cassette('lfs-authenticate-ok-download') do
lfs_access = gitlab_net.lfs_authenticate(key, project, 'download')
expect(lfs_access.username).to eq('root')
expect(lfs_access.lfs_token).to eq('Hyzhyde_wLUeyUQsR3tHGTG8eNocVQm4ssioTEsBSdb6KwCSzQ')
expect(lfs_access.repository_http_path).to eq(repository_http_path)
end
end
end
context 'for upload operation' do
it 'should return the correct data' do
VCR.use_cassette('lfs-authenticate-ok-upload') do
lfs_access = gitlab_net.lfs_authenticate(key, project, 'upload')
expect(lfs_access.username).to eq('root')
expect(lfs_access.lfs_token).to eq('Hyzhyde_wLUeyUQsR3tHGTG8eNocVQm4ssioTEsBSdb6KwCSzQ')
expect(lfs_access.repository_http_path).to eq(repository_http_path)
end
end
end
end
end
describe '#broadcast_message' do
context "broadcast message exists" do
it 'should return message' do
VCR.use_cassette("broadcast_message-ok") do
result = gitlab_net.broadcast_message
expect(result["message"]).to eq("Message")
end
end
end
context "broadcast message doesn't exist" do
it 'should return nil' do
VCR.use_cassette("broadcast_message-none") do
result = gitlab_net.broadcast_message
expect(result).to eq({})
end
end
end
end
describe '#merge_request_urls' do
let(:gl_repository) { "project-1" }
let(:changes) { "123456 789012 refs/heads/test\n654321 210987 refs/tags/tag" }
let(:encoded_changes) { "123456%20789012%20refs/heads/test%0A654321%20210987%20refs/tags/tag" }
it "sends the given arguments as encoded URL parameters" do
expect(gitlab_net).to receive(:get).with("#{internal_api_endpoint}/merge_request_urls?project=#{project}&changes=#{encoded_changes}&gl_repository=#{gl_repository}")
gitlab_net.merge_request_urls(gl_repository, project, changes)
end
it "omits the gl_repository parameter if it's nil" do
expect(gitlab_net).to receive(:get).with("#{internal_api_endpoint}/merge_request_urls?project=#{project}&changes=#{encoded_changes}")
gitlab_net.merge_request_urls(nil, project, changes)
end
it "returns an empty array when the result cannot be parsed as JSON" do
response = double(:response, code: '200', body: '')
allow(gitlab_net).to receive(:get).and_return(response)
expect(gitlab_net.merge_request_urls(gl_repository, project, changes)).to eq([])
end
it "returns an empty array when the result's status is not 200" do
response = double(:response, code: '500', body: '[{}]')
allow(gitlab_net).to receive(:get).and_return(response)
expect(gitlab_net.merge_request_urls(gl_repository, project, changes)).to eq([])
end
end
describe '#pre_receive' do
let(:gl_repository) { "project-1" }
let(:params) { { gl_repository: gl_repository } }
subject { gitlab_net.pre_receive(gl_repository) }
it 'sends the correct parameters and returns the request body parsed' do
expect_any_instance_of(Net::HTTP::Post).to receive(:set_form_data)
.with(hash_including(params))
VCR.use_cassette("pre-receive") { subject }
end
it 'calls /internal/pre-receive' do
VCR.use_cassette("pre-receive") do
expect(subject['reference_counter_increased']).to be(true)
end
end
it 'throws a NotFound error when pre-receive is not available' do
VCR.use_cassette("pre-receive-not-found") do
expect { subject }.to raise_error(GitlabNet::NotFound)
end
end
end
describe '#post_receive' do
let(:gl_repository) { "project-1" }
let(:changes) { "123456 789012 refs/heads/test\n654321 210987 refs/tags/tag" }
let(:push_options) { ["ci-skip", "something unexpected"] }
let(:params) do
{ gl_repository: gl_repository, identifier: key, changes: changes, :"push_options[]" => push_options }
end
let(:merge_request_urls) do
[{
"branch_name" => "test",
"url" => "http://localhost:3000/gitlab-org/gitlab-test/merge_requests/7",
"new_merge_request" => false
}]
end
subject { gitlab_net.post_receive(gl_repository, key, changes, push_options) }
it 'sends the correct parameters' do
expect_any_instance_of(Net::HTTP::Post).to receive(:set_form_data).with(hash_including(params))
VCR.use_cassette("post-receive") do
subject
end
end
it 'calls /internal/post-receive' do
VCR.use_cassette("post-receive") do
expect(subject['merge_request_urls']).to eq(merge_request_urls)
expect(subject['broadcast_message']).to eq('Message')
expect(subject['reference_counter_decreased']).to eq(true)
end
end
it 'throws a NotFound error when post-receive is not available' do
VCR.use_cassette("post-receive-not-found") do
expect { subject }.to raise_error(GitlabNet::NotFound)
end
end
end
describe '#authorized_key' do
let (:ssh_key) { "rsa-key" }
it "should return nil when the resource is not implemented" do
VCR.use_cassette("ssh-key-not-implemented") do
result = gitlab_net.authorized_key("whatever")
expect(result).to be_nil
end
end
it "should return nil when the fingerprint is not found" do
VCR.use_cassette("ssh-key-not-found") do
result = gitlab_net.authorized_key("whatever")
expect(result).to be_nil
end
end
it "should return a ssh key with a valid fingerprint" do
VCR.use_cassette("ssh-key-ok") do
result = gitlab_net.authorized_key(ssh_key)
expect(result).to eq({
"can_push" => false,
"created_at" => "2017-06-21T09:50:07.150Z",
"id" => 99,
"key" => "ssh-rsa rsa-key dummy@gitlab.com",
"title" => "untitled"
})
end
end
end
describe '#two_factor_recovery_codes' do
it 'returns two factor recovery codes' do
VCR.use_cassette('two-factor-recovery-codes') do
result = gitlab_net.two_factor_recovery_codes(key)
expect(result['success']).to be_truthy
expect(result['recovery_codes']).to eq(['f67c514de60c4953','41278385fc00c1e0'])
end
end
it 'returns false when recovery codes cannot be generated' do
VCR.use_cassette('two-factor-recovery-codes-fail') do
result = gitlab_net.two_factor_recovery_codes('key-777')
expect(result['success']).to be_falsey
expect(result['message']).to eq('Could not find the given key')
end
end
end
describe '#notify_post_receive' do
let(:gl_repository) { 'project-1' }
let(:repo_path) { '/path/to/my/repo.git' }
let(:params) do
{ gl_repository: gl_repository, project: repo_path }
end
it 'sets the arguments as form parameters' do
VCR.use_cassette('notify-post-receive') do
expect_any_instance_of(Net::HTTP::Post).to receive(:set_form_data).with(hash_including(params))
gitlab_net.notify_post_receive(gl_repository, repo_path)
end
end
it 'returns true if notification was succesful' do
VCR.use_cassette('notify-post-receive') do
expect(gitlab_net.notify_post_receive(gl_repository, repo_path)).to be_truthy
end
end
end
describe '#check_access' do
context 'ssh key with access nil, to project' 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.allowed?).to be_truthy
expect(access.gl_project_path).to eq('gitlab-org/gitlab.test')
end
end
context 'but project not found' do
where(:desc, :cassette, :message) do
'deny push access for host' | 'allowed-push-project-not-found' | 'The project you were looking for could not be found.'
'deny push access for host (when text/html)' | 'allowed-push-project-not-found-text-html' | 'API is not accessible'
'deny push access for host (when text/plain)' | 'allowed-push-project-not-found-text-plain' | 'API is not accessible'
'deny push access for host (when 404 is returned)' | 'allowed-push-project-not-found-404' | 'The project you were looking for could not be found.'
'deny push access for host (when 404 is returned with text/html)' | 'allowed-push-project-not-found-404-text-html' | 'API is not accessible'
'deny push access for host (when 404 is returned with text/plain)' | 'allowed-push-project-not-found-404-text-plain' | 'API is not accessible'
end
with_them do
it 'should deny push access for host' do
VCR.use_cassette(cassette) do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
expect(access.allowed?).to be_falsey
expect(access.message).to eql(message)
end
end
end
end
it 'adds the secret_token to the request' do
VCR.use_cassette("allowed-push") do
expect_any_instance_of(Net::HTTP::Post).to receive(:set_form_data).with(hash_including(secret_token: secret))
gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
end
end
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.allowed?).to be_truthy
expect(access.gl_project_path).to eq('gitlab-org/gitlab.test')
end
end
end
context 'ssh access has been disabled' do
it 'should deny pull access for host' do
VCR.use_cassette('ssh-pull-disabled') do
access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh')
expect(access.allowed?).to be_falsey
expect(access.message).to eq 'Git access over SSH is not allowed'
end
end
it 'should deny push access for host' do
VCR.use_cassette('ssh-push-disabled') do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
expect(access.allowed?).to be_falsey
expect(access.message).to eq 'Git access over SSH is not allowed'
end
end
end
context 'http access has been disabled' do
it 'should deny pull access for host' do
VCR.use_cassette('http-pull-disabled') do
access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http')
expect(access.allowed?).to be_falsey
expect(access.message).to eq 'Pulling over HTTP is not allowed.'
end
end
it 'should deny push access for host' do
VCR.use_cassette("http-push-disabled") do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'http')
expect(access.allowed?).to be_falsey
expect(access.message).to eq 'Pushing over HTTP is not allowed.'
end
end
end
context 'ssh key without access to project' do
where(:desc, :cassette, :message) do
'deny push access for host' | 'ssh-push-project-denied' | 'Git access over SSH is not allowed'
'deny push access for host (when 401 is returned)' | 'ssh-push-project-denied-401' | 'Git access over SSH is not allowed'
'deny push access for host (when 401 is returned with text/html)' | 'ssh-push-project-denied-401-text-html' | 'API is not accessible'
'deny push access for host (when 401 is returned with text/plain)' | 'ssh-push-project-denied-401-text-plain' | 'API is not accessible'
'deny pull access for host' | 'ssh-pull-project-denied' | 'Git access over SSH is not allowed'
'deny pull access for host (when 401 is returned)' | 'ssh-pull-project-denied-401' | 'Git access over SSH is not allowed'
'deny pull access for host (when 401 is returned with text/html)' | 'ssh-pull-project-denied-401-text-html' | 'API is not accessible'
'deny pull access for host (when 401 is returned with text/plain)' | 'ssh-pull-project-denied-401-text-plain' | 'API is not accessible'
end
with_them do
it 'should deny push access for host' do
VCR.use_cassette(cassette) do
access = gitlab_net.check_access('git-receive-pack', nil, project, key2, changes, 'ssh')
expect(access.allowed?).to be_falsey
expect(access.message).to eql(message)
end
end
end
it 'should deny pull access for host (with user)' do
VCR.use_cassette("ssh-pull-project-denied-with-user") do
access = gitlab_net.check_access('git-upload-pack', nil, project, 'user-2', changes, 'ssh')
expect(access.allowed?).to be_falsey
expect(access.message).to eql('Git access over SSH is not allowed')
end
end
end
it 'handles non 200 status codes' do
resp = double(:resp, code: 501)
allow(gitlab_net).to receive(:post).and_return(resp)
access = gitlab_net.check_access('git-upload-pack', nil, project, 'user-2', changes, 'ssh')
expect(access).not_to be_allowed
end
it "raises an exception if the connection fails" do
allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError)
expect {
gitlab_net.check_access('git-upload-pack', nil, project, 'user-1', changes, 'ssh')
}.to raise_error(GitlabNet::ApiUnreachableError)
end
end
describe '#base_api_endpoint' do
let(:net) { described_class.new }
subject { net.send :base_api_endpoint }
it { is_expected.to include(net.send(:config).gitlab_url) }
it("uses API version 4") { is_expected.to end_with("api/v4") }
end
describe '#internal_api_endpoint' do
let(:net) { described_class.new }
subject { net.send :internal_api_endpoint }
it { is_expected.to include(net.send(:config).gitlab_url) }
it("uses API version 4") { is_expected.to end_with("api/v4/internal") }
end
describe '#http_client_for' do
subject { gitlab_net.send :http_client_for, URI('https://localhost/') }
before do
allow(gitlab_net).to receive :cert_store
allow(gitlab_net.send(:config)).to receive(:http_settings) { {'self_signed_cert' => true} }
end
it { expect(subject.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) }
end
describe '#http_request_for' do
context 'with stub' do
let(:get) { double(Net::HTTP::Get) }
let(:user) { 'user' }
let(:password) { 'password' }
let(:url) { URI 'http://localhost/' }
let(:params) { { 'key1' => 'value1' } }
let(:headers) { { 'Content-Type' => 'application/json'} }
let(:options) { { json: { 'key2' => 'value2' } } }
context 'with no params, options or headers' do
subject { gitlab_net.send :http_request_for, :get, url }
before do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('user') { user }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('password') { password }
expect(Net::HTTP::Get).to receive(:new).with('/', {}).and_return(get)
expect(get).to receive(:basic_auth).with(user, password).once
expect(get).to receive(:set_form_data).with(hash_including(secret_token: secret)).once
end
it { is_expected.not_to be_nil }
end
context 'with params' do
subject { gitlab_net.send :http_request_for, :get, url, params: params, headers: headers }
before do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('user') { user }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('password') { password }
expect(Net::HTTP::Get).to receive(:new).with('/', headers).and_return(get)
expect(get).to receive(:basic_auth).with(user, password).once
expect(get).to receive(:set_form_data).with({ 'key1' => 'value1', secret_token: secret }).once
end
it { is_expected.not_to be_nil }
end
context 'with headers' do
subject { gitlab_net.send :http_request_for, :get, url, headers: headers }
before do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('user') { user }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('password') { password }
expect(Net::HTTP::Get).to receive(:new).with('/', headers).and_return(get)
expect(get).to receive(:basic_auth).with(user, password).once
expect(get).to receive(:set_form_data).with(hash_including(secret_token: secret)).once
end
it { is_expected.not_to be_nil }
end
context 'with options' do
context 'with json' do
subject { gitlab_net.send :http_request_for, :get, url, options: options }
before do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('user') { user }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('password') { password }
expect(Net::HTTP::Get).to receive(:new).with('/', {}).and_return(get)
expect(get).to receive(:basic_auth).with(user, password).once
expect(get).to receive(:body=).with({ 'key2' => 'value2', secret_token: secret }.to_json).once
expect(get).not_to receive(:set_form_data)
end
it { is_expected.not_to be_nil }
end
end
end
context 'Unix socket' do
it 'sets the Host header to "localhost"' do
gitlab_net = described_class.new
expect(gitlab_net).to receive(:secret_token).and_return(secret)
request = gitlab_net.send(:http_request_for, :get, URI('http+unix://%2Ffoo'))
expect(request['Host']).to eq('localhost')
end
end
end
describe '#cert_store' do
let(:store) do
double(OpenSSL::X509::Store).tap do |store|
allow(OpenSSL::X509::Store).to receive(:new) { store }
end
end
before :each do
expect(store).to receive(:set_default_paths).once
end
after do
gitlab_net.send :cert_store
end
it "calls add_file with http_settings['ca_file']" do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('ca_file') { 'test_file' }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('ca_path') { nil }
expect(store).to receive(:add_file).with('test_file')
expect(store).not_to receive(:add_path)
end
it "calls add_path with http_settings['ca_path']" do
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('ca_file') { nil }
allow(gitlab_net.send(:config).http_settings).to receive(:[]).with('ca_path') { 'test_path' }
expect(store).not_to receive(:add_file)
expect(store).to receive(:add_path).with('test_path')
end
end
end
...@@ -3,13 +3,17 @@ require_relative 'spec_helper' ...@@ -3,13 +3,17 @@ require_relative 'spec_helper'
describe 'bin/gitlab-shell-authorized-principals-check' do describe 'bin/gitlab-shell-authorized-principals-check' do
include_context 'gitlab shell' include_context 'gitlab shell'
before(:all) do
write_config({})
end
def mock_server(server) def mock_server(server)
# Do nothing as we're not connecting to a server in this check. # Do nothing as we're not connecting to a server in this check.
end end
let(:authorized_principals_check_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell-authorized-principals-check') } let(:authorized_principals_check_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell-authorized-principals-check') }
shared_examples 'authorized principals check' do describe 'authorized principals check' do
it 'succeeds when a valid principal is given' do it 'succeeds when a valid principal is given' do
output, status = run! output, status = run!
...@@ -39,35 +43,6 @@ describe 'bin/gitlab-shell-authorized-principals-check' do ...@@ -39,35 +43,6 @@ describe 'bin/gitlab-shell-authorized-principals-check' do
end end
end end
describe 'without go features' do
before(:all) do
write_config({})
end
it_behaves_like 'authorized principals check'
end
describe 'without go features (via go)', :go do
before(:all) do
write_config({})
end
it_behaves_like 'authorized principals check'
end
describe 'with the go authorized-principals-check feature', :go do
before(:all) do
write_config(
'migration' => {
'enabled' => true,
'features' => ['authorized-principals-check']
}
)
end
it_behaves_like 'authorized principals check'
end
def run!(key_id: 'key', principals: ['principal']) def run!(key_id: 'key', principals: ['principal'])
cmd = [ cmd = [
authorized_principals_check_path, authorized_principals_check_path,
......
...@@ -7,6 +7,12 @@ require 'base64' ...@@ -7,6 +7,12 @@ require 'base64'
describe 'Custom bin/gitlab-shell git-receive-pack' do describe 'Custom bin/gitlab-shell git-receive-pack' do
include_context 'gitlab shell' include_context 'gitlab shell'
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => 'git-receive-pack group/repo' } }
before(:context) do
write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}")
end
def mock_server(server) def mock_server(server)
server.mount_proc('/geo/proxy_git_push_ssh/info_refs') do |req, res| server.mount_proc('/geo/proxy_git_push_ssh/info_refs') do |req, res|
res.content_type = 'application/json' res.content_type = 'application/json'
...@@ -58,7 +64,9 @@ describe 'Custom bin/gitlab-shell git-receive-pack' do ...@@ -58,7 +64,9 @@ describe 'Custom bin/gitlab-shell git-receive-pack' do
end end
end end
shared_examples 'dialog for performing a custom action' do describe 'dialog for performing a custom action' do
let(:inaccessible_error) { "Internal API error (403)\n" }
context 'when API calls perform successfully' do context 'when API calls perform successfully' do
def verify_successful_call!(cmd) def verify_successful_call!(cmd)
Open3.popen3(env, cmd) do |stdin, stdout, stderr| Open3.popen3(env, cmd) do |stdin, stdout, stderr|
...@@ -103,32 +111,4 @@ describe 'Custom bin/gitlab-shell git-receive-pack' do ...@@ -103,32 +111,4 @@ describe 'Custom bin/gitlab-shell git-receive-pack' do
end end
end end
end end
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => 'git-receive-pack group/repo' } }
describe 'without go features' do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
)
end
it_behaves_like 'dialog for performing a custom action' do
let(:inaccessible_error) { "> GitLab: API is not accessible\n" }
end
end
describe 'with go features', :go do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
"migration" => { "enabled" => true,
"features" => ["git-receive-pack"] }
)
end
it_behaves_like 'dialog for performing a custom action' do
let(:inaccessible_error) { "Internal API error (403)\n" }
end
end
end end
...@@ -5,6 +5,10 @@ require 'open3' ...@@ -5,6 +5,10 @@ require 'open3'
describe 'bin/gitlab-shell' do describe 'bin/gitlab-shell' do
include_context 'gitlab shell' include_context 'gitlab shell'
before(:context) do
write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}")
end
def mock_server(server) def mock_server(server)
server.mount_proc('/api/v4/internal/discover') do |req, res| server.mount_proc('/api/v4/internal/discover') do |req, res|
identifier = req.query['key_id'] || req.query['username'] || req.query['user_id'] identifier = req.query['key_id'] || req.query['username'] || req.query['user_id']
...@@ -35,7 +39,7 @@ describe 'bin/gitlab-shell' do ...@@ -35,7 +39,7 @@ describe 'bin/gitlab-shell' do
Open3.capture3(env, cmd) Open3.capture3(env, cmd)
end end
shared_examples 'results with keys' do describe 'results with keys' do
# Basic valid input # Basic valid input
it 'succeeds and prints username when a valid known key id is given' do it 'succeeds and prints username when a valid known key id is given' do
output, _, status = run!(["key-100"]) output, _, status = run!(["key-100"])
...@@ -102,26 +106,6 @@ describe 'bin/gitlab-shell' do ...@@ -102,26 +106,6 @@ describe 'bin/gitlab-shell' do
expect(output).to eq("Welcome to GitLab, @someuser!\n") expect(output).to eq("Welcome to GitLab, @someuser!\n")
expect(status).to be_success expect(status).to be_success
end end
end
describe 'without go features' do
before(:context) do
write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}")
end
it_behaves_like 'results with keys'
end
describe 'with the go discover feature', :go do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
"migration" => { "enabled" => true,
"features" => ["discover"] }
)
end
it_behaves_like 'results with keys'
it 'outputs "Only SSH allowed"' do it 'outputs "Only SSH allowed"' do
_, stderr, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "username-someuser"], env: {}) _, stderr, status = run!(["-c/usr/share/webapps/gitlab-shell/bin/gitlab-shell", "username-someuser"], env: {})
......
...@@ -6,6 +6,11 @@ describe 'bin/gitlab-shell git-lfs-authentication' do ...@@ -6,6 +6,11 @@ describe 'bin/gitlab-shell git-lfs-authentication' do
include_context 'gitlab shell' include_context 'gitlab shell'
let(:path) { "https://gitlab.com/repo/path" } let(:path) { "https://gitlab.com/repo/path" }
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => 'git-lfs-authenticate project/repo download' } }
before(:context) do
write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}")
end
def mock_server(server) def mock_server(server)
server.mount_proc('/api/v4/internal/lfs_authenticate') do |req, res| server.mount_proc('/api/v4/internal/lfs_authenticate') do |req, res|
...@@ -49,7 +54,7 @@ describe 'bin/gitlab-shell git-lfs-authentication' do ...@@ -49,7 +54,7 @@ describe 'bin/gitlab-shell git-lfs-authentication' do
end end
end end
shared_examples 'lfs authentication command' do describe 'lfs authentication command' do
def successful_response def successful_response
{ {
"header" => { "header" => {
...@@ -119,28 +124,4 @@ describe 'bin/gitlab-shell git-lfs-authentication' do ...@@ -119,28 +124,4 @@ describe 'bin/gitlab-shell git-lfs-authentication' do
end end
end end
end end
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => 'git-lfs-authenticate project/repo download' } }
describe 'without go features' do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
)
end
it_behaves_like 'lfs authentication command'
end
describe 'with go features' do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
"migration" => { "enabled" => true,
"features" => ["git-lfs-authenticate"] }
)
end
it_behaves_like 'lfs authentication command'
end
end end
require_relative 'spec_helper'
require_relative '../lib/gitlab_shell'
require_relative '../lib/gitlab_access_status'
describe GitlabShell do
before do
$logger = double('logger').as_null_object
FileUtils.mkdir_p(tmp_repos_path)
end
after do
FileUtils.rm_rf(tmp_repos_path)
end
subject do
ARGV[0] = gl_id
GitlabShell.new(gl_id).tap do |shell|
allow(shell).to receive(:exec_cmd).and_return(:exec_called)
allow(shell).to receive(:api).and_return(api)
end
end
let(:git_config_options) { ['receive.MaxInputSize=10000'] }
let(:gitaly_check_access) do
GitAccessStatus.new(
true,
'200',
'ok',
gl_repository: gl_repository,
gl_project_path: gl_project_path,
gl_id: gl_id,
gl_username: gl_username,
git_config_options: git_config_options,
gitaly: { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' },
git_protocol: git_protocol,
gl_console_messages: gl_console_messages
)
end
let(:api) do
double(GitlabNet).tap do |api|
allow(api).to receive(:discover).and_return({ 'name' => 'John Doe', 'username' => 'testuser' })
allow(api).to receive(:check_access).and_return(GitAccessStatus.new(
true,
'200',
'ok',
gl_repository: gl_repository,
gl_project_path: gl_project_path,
gl_id: gl_id,
gl_username: gl_username,
git_config_options: nil,
gitaly: nil,
git_protocol: git_protocol))
allow(api).to receive(:two_factor_recovery_codes).and_return({
'success' => true,
'recovery_codes' => %w[f67c514de60c4953 41278385fc00c1e0]
})
end
end
let(:gl_id) { "key-#{rand(100) + 100}" }
let(:ssh_cmd) { nil }
let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') }
let(:repo_name) { 'gitlab-ci.git' }
let(:gl_repository) { 'project-1' }
let(:gl_project_path) { 'group/subgroup/gitlab-ci' }
let(:gl_id) { 'user-1' }
let(:gl_username) { 'testuser' }
let(:git_config_options) { ['receive.MaxInputSize=10000'] }
let(:git_protocol) { 'version=2' }
let(:gl_console_messages) { nil }
before do
allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(false)
end
describe '#initialize' do
let(:ssh_cmd) { 'git-receive-pack' }
it { expect(subject.gl_id).to eq gl_id }
end
describe '#parse_cmd' do
describe 'git' do
context 'w/o namespace' do
let(:ssh_args) { %w(git-upload-pack gitlab-ci.git) }
before do
subject.send :parse_cmd, ssh_args
end
it 'has the correct attributes' do
expect(subject.repo_name).to eq 'gitlab-ci.git'
expect(subject.command).to eq 'git-upload-pack'
end
end
context 'namespace' do
let(:repo_name) { 'dmitriy.zaporozhets/gitlab-ci.git' }
let(:ssh_args) { %w(git-upload-pack dmitriy.zaporozhets/gitlab-ci.git) }
before do
subject.send :parse_cmd, ssh_args
end
it 'has the correct attributes' do
expect(subject.repo_name).to eq 'dmitriy.zaporozhets/gitlab-ci.git'
expect(subject.command).to eq 'git-upload-pack'
end
end
context 'with an invalid number of arguments' do
let(:ssh_args) { %w(foobar) }
it "should raise an DisallowedCommandError" do
expect { subject.send :parse_cmd, ssh_args }.to raise_error(GitlabShell::DisallowedCommandError)
end
end
context 'with an API command' do
before do
subject.send :parse_cmd, ssh_args
end
context 'when generating recovery codes' do
let(:ssh_args) { %w(2fa_recovery_codes) }
it 'sets the correct command' do
expect(subject.command).to eq('2fa_recovery_codes')
end
it 'does not set repo name' do
expect(subject.repo_name).to be_nil
end
end
end
end
describe 'git-lfs' do
let(:repo_name) { 'dzaporozhets/gitlab.git' }
let(:ssh_args) { %w(git-lfs-authenticate dzaporozhets/gitlab.git download) }
before do
subject.send :parse_cmd, ssh_args
end
it 'has the correct attributes' do
expect(subject.repo_name).to eq 'dzaporozhets/gitlab.git'
expect(subject.command).to eq 'git-lfs-authenticate'
expect(subject.git_access).to eq 'git-upload-pack'
end
end
describe 'git-lfs old clients' do
let(:repo_name) { 'dzaporozhets/gitlab.git' }
let(:ssh_args) { %w(git-lfs-authenticate dzaporozhets/gitlab.git download long_oid) }
before do
subject.send :parse_cmd, ssh_args
end
it 'has the correct attributes' do
expect(subject.repo_name).to eq 'dzaporozhets/gitlab.git'
expect(subject.command).to eq 'git-lfs-authenticate'
expect(subject.git_access).to eq 'git-upload-pack'
end
end
end
describe '#exec' do
let(:gitaly_message) do
JSON.dump(
'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' },
'gl_repository' => gl_repository,
'gl_project_path' => gl_project_path,
'gl_id' => gl_id,
'gl_username' => gl_username,
'git_config_options' => git_config_options,
'git_protocol' => git_protocol
)
end
before do
allow(ENV).to receive(:[]).with('GIT_PROTOCOL').and_return(git_protocol)
end
shared_examples_for 'upload-pack' do |command|
let(:ssh_cmd) { "#{command} gitlab-ci.git" }
after { subject.exec(ssh_cmd) }
it "should process the command" do
expect(subject).to receive(:process_cmd).with(%w(git-upload-pack gitlab-ci.git))
end
it "should execute the command" do
expect(subject).to receive(:exec_cmd).with('git-upload-pack')
end
it "should log the command execution" do
message = "executing git command"
user_string = "user with id #{gl_id}"
expect($logger).to receive(:info).with(message, command: "git-upload-pack", user: user_string)
end
it "should use usernames if configured to do so" do
allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(true)
expect($logger).to receive(:info).with("executing git command", hash_including(user: 'testuser'))
end
end
context 'gitaly-upload-pack' do
let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
before do
allow(api).to receive(:check_access).and_return(gitaly_check_access)
end
after { subject.exec(ssh_cmd) }
it "should process the command" do
expect(subject).to receive(:process_cmd).with(%w(git-upload-pack gitlab-ci.git))
end
it "should execute the command" do
expect(subject).to receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-upload-pack"), gitaly_address: 'unix:gitaly.socket', json_args: gitaly_message, token: nil)
end
it "should log the command execution" do
message = "executing git command"
user_string = "user with id #{gl_id}"
expect($logger).to receive(:info).with(message, command: "gitaly-upload-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
end
it "should use usernames if configured to do so" do
allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(true)
expect($logger).to receive(:info).with("executing git command", hash_including(user: 'testuser'))
end
end
context 'git-receive-pack' do
let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
before do
allow(api).to receive(:check_access).and_return(gitaly_check_access)
end
after { subject.exec(ssh_cmd) }
it "should process the command" do
expect(subject).to receive(:process_cmd).with(%w(git-receive-pack gitlab-ci.git))
end
it "should execute the command" do
expect(subject).to receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-receive-pack"), gitaly_address: 'unix:gitaly.socket', json_args: gitaly_message, token: nil)
end
it "should log the command execution" do
message = "executing git command"
user_string = "user with id #{gl_id}"
expect($logger).to receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
end
context 'with a custom action' do
let(:fake_payload) { { 'api_endpoints' => [ '/fake/api/endpoint' ], 'data' => {} } }
let(:custom_action_gitlab_access_status) do
GitAccessStatus.new(
true,
'300',
'Multiple Choices',
payload: fake_payload
)
end
let(:action_custom) { double(Action::Custom) }
before do
allow(api).to receive(:check_access).and_return(custom_action_gitlab_access_status)
end
it "should not process the command" do
expect(subject).to_not receive(:process_cmd).with(%w(git-receive-pack gitlab-ci.git))
expect(Action::Custom).to receive(:new).with(gl_id, fake_payload).and_return(action_custom)
expect(action_custom).to receive(:execute)
end
end
end
context 'gitaly-receive-pack' do
let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
before do
allow(api).to receive(:check_access).and_return(gitaly_check_access)
end
after { subject.exec(ssh_cmd) }
it "should process the command" do
expect(subject).to receive(:process_cmd).with(%w(git-receive-pack gitlab-ci.git))
end
it "should execute the command" do
expect(subject).to receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-receive-pack"), gitaly_address: 'unix:gitaly.socket', json_args: gitaly_message, token: nil)
end
it "should log the command execution" do
message = "executing git command"
user_string = "user with id #{gl_id}"
expect($logger).to receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
end
it "should use usernames if configured to do so" do
allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(true)
expect($logger).to receive(:info).with("executing git command", hash_including(user: 'testuser'))
end
end
shared_examples_for 'upload-archive' do |command|
let(:ssh_cmd) { "#{command} gitlab-ci.git" }
let(:exec_cmd_log_params) { exec_cmd_params }
after { subject.exec(ssh_cmd) }
it "should process the command" do
expect(subject).to receive(:process_cmd).with(%w(git-upload-archive gitlab-ci.git))
end
it "should execute the command" do
expect(subject).to receive(:exec_cmd).with(*exec_cmd_params)
end
it "should log the command execution" do
message = "executing git command"
user_string = "user with id #{gl_id}"
expect($logger).to receive(:info).with(message, command: exec_cmd_log_params.join(' '), user: user_string)
end
it "should use usernames if configured to do so" do
allow_any_instance_of(GitlabConfig).to receive(:audit_usernames).and_return(true)
expect($logger).to receive(:info).with("executing git command", hash_including(user: 'testuser'))
end
end
context 'gitaly-upload-archive' do
before do
allow(api).to receive(:check_access).and_return(gitaly_check_access)
end
it_behaves_like 'upload-archive', 'git-upload-archive' do
let(:gitaly_executable) { "gitaly-upload-archive" }
let(:exec_cmd_params) do
[
File.join(ROOT_PATH, "bin", gitaly_executable),
{ gitaly_address: 'unix:gitaly.socket', json_args: gitaly_message, token: nil }
]
end
let(:exec_cmd_log_params) do
[gitaly_executable, 'unix:gitaly.socket', gitaly_message]
end
end
end
context 'arbitrary command' do
let(:ssh_cmd) { 'arbitrary command' }
after { subject.exec(ssh_cmd) }
it "should not process the command" do
expect(subject).not_to receive(:process_cmd)
end
it "should not execute the command" do
expect(subject).not_to receive(:exec_cmd)
end
it "should log the attempt" do
message = 'Denied disallowed command'
user_string = "user with id #{gl_id}"
expect($logger).to receive(:warn).with(message, command: 'arbitrary command', user: user_string)
end
end
context 'no command' do
after { subject.exec(nil) }
it "should call api.discover" do
expect(api).to receive(:discover).with(gl_id)
end
end
context "failed connection" do
let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' }
before do
allow(api).to receive(:check_access).and_raise(GitlabNet::ApiUnreachableError)
end
after { subject.exec(ssh_cmd) }
it "should not process the command" do
expect(subject).not_to receive(:process_cmd)
end
it "should not execute the command" do
expect(subject).not_to receive(:exec_cmd)
end
end
context 'with an API command' do
before do
allow(subject).to receive(:continue?).and_return(true)
end
context 'when generating recovery codes' do
let(:ssh_cmd) { '2fa_recovery_codes' }
after do
subject.exec(ssh_cmd)
end
it 'does not call verify_access' do
expect(subject).not_to receive(:verify_access)
end
it 'calls the corresponding method' do
expect(subject).to receive(:api_2fa_recovery_codes)
end
it 'outputs recovery codes' do
expect($stdout).to receive(:puts)
.with(/f67c514de60c4953\n41278385fc00c1e0/)
end
context 'when the process is unsuccessful' do
it 'displays the error to the user' do
allow(api).to receive(:two_factor_recovery_codes).and_return({
'success' => false,
'message' => 'Could not find the given key'
})
expect($stdout).to receive(:puts)
.with(/Could not find the given key/)
end
end
end
end
context 'with a console message' do
let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
let(:gl_console_messages) { 'Very important message' }
before do
allow(api).to receive(:check_access).and_return(gitaly_check_access)
end
it 'displays the message on $stderr' do
expect { subject.exec(ssh_cmd) }.to output("> GitLab: #{gl_console_messages}\n").to_stderr
end
end
end
describe '#validate_access' do
let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
describe 'check access with api' do
before do
allow(api).to receive(:check_access).and_return(
GitAccessStatus.new(
false,
'denied',
gl_repository: nil,
gl_project_path: nil,
gl_id: nil,
gl_username: nil,
git_config_options: nil,
gitaly: nil,
git_protocol: nil))
end
after { subject.exec(ssh_cmd) }
it "should call api.check_access" do
expect(api).to receive(:check_access).with('git-upload-pack', nil, 'gitlab-ci.git', gl_id, '_any', 'ssh')
end
it "should disallow access and log the attempt if check_access returns false status" do
message = 'Access denied'
user_string = "user with id #{gl_id}"
expect($logger).to receive(:warn).with(message, command: 'git-upload-pack gitlab-ci.git', user: user_string)
end
end
end
describe '#api' do
let(:shell) { GitlabShell.new(gl_id) }
subject { shell.send :api }
it { is_expected.to be_a(GitlabNet) }
end
end
...@@ -5,6 +5,12 @@ require 'open3' ...@@ -5,6 +5,12 @@ require 'open3'
describe 'bin/gitlab-shell 2fa_recovery_codes' do describe 'bin/gitlab-shell 2fa_recovery_codes' do
include_context 'gitlab shell' include_context 'gitlab shell'
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => '2fa_recovery_codes' } }
before(:context) do
write_config("gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}")
end
def mock_server(server) def mock_server(server)
server.mount_proc('/api/v4/internal/two_factor_recovery_codes') do |req, res| server.mount_proc('/api/v4/internal/two_factor_recovery_codes') do |req, res|
res.content_type = 'application/json' res.content_type = 'application/json'
...@@ -31,7 +37,7 @@ describe 'bin/gitlab-shell 2fa_recovery_codes' do ...@@ -31,7 +37,7 @@ describe 'bin/gitlab-shell 2fa_recovery_codes' do
end end
end end
shared_examples 'dialog for regenerating recovery keys' do describe 'dialog for regenerating recovery keys' do
context 'when the user agrees to regenerate keys' do context 'when the user agrees to regenerate keys' do
def verify_successful_regeneration!(cmd) def verify_successful_regeneration!(cmd)
Open3.popen2(env, cmd) do |stdin, stdout| Open3.popen2(env, cmd) do |stdin, stdout|
...@@ -101,28 +107,4 @@ describe 'bin/gitlab-shell 2fa_recovery_codes' do ...@@ -101,28 +107,4 @@ describe 'bin/gitlab-shell 2fa_recovery_codes' do
end end
end end
end end
let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => '2fa_recovery_codes' } }
describe 'without go features' do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
)
end
it_behaves_like 'dialog for regenerating recovery keys'
end
describe 'with go features', :go do
before(:context) do
write_config(
"gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
"migration" => { "enabled" => true,
"features" => ["2fa_recovery_codes"] }
)
end
it_behaves_like 'dialog for regenerating recovery keys'
end
end end
require_relative 'spec_helper'
require_relative '../lib/hooks_utils.rb'
describe :get_push_options do
context "when GIT_PUSH_OPTION_COUNT is not set" do
it { expect(HooksUtils.get_push_options).to eq([]) }
end
context "when one option is given" do
before do
ENV['GIT_PUSH_OPTION_COUNT'] = '1'
ENV['GIT_PUSH_OPTION_0'] = 'aaa'
end
it { expect(HooksUtils.get_push_options).to eq(['aaa']) }
end
context "when multiple options are given" do
before do
ENV['GIT_PUSH_OPTION_COUNT'] = '3'
ENV['GIT_PUSH_OPTION_0'] = 'aaa'
ENV['GIT_PUSH_OPTION_1'] = 'bbb'
ENV['GIT_PUSH_OPTION_2'] = 'ccc'
end
it { expect(HooksUtils.get_push_options).to eq(['aaa', 'bbb', 'ccc']) }
end
end
require_relative 'spec_helper'
require_relative '../lib/httpunix'
describe URI::HTTPUNIX do
describe :parse do
uri = URI::parse('http+unix://%2Fpath%2Fto%2Fsocket/img.jpg')
subject { uri }
it { is_expected.to be_an_instance_of(URI::HTTPUNIX) }
it 'has the correct attributes' do
expect(subject.scheme).to eq('http+unix')
expect(subject.hostname).to eq('/path/to/socket')
expect(subject.path).to eq('/img.jpg')
end
end
end
describe Net::HTTPUNIX do
def tmp_socket_path
# This has to be a relative path shorter than 100 bytes due to
# limitations in how Unix sockets work.
'tmp/test-socket'
end
before(:all) do
# "hello world" over unix socket server in background thread
FileUtils.mkdir_p(File.dirname(tmp_socket_path))
@server = HTTPUNIXServer.new(BindAddress: tmp_socket_path)
@server.mount_proc '/' do |req, resp|
resp.body = "Hello World (at #{req.path})"
end
@webrick_thread = Thread.new { @server.start }
sleep(0.1) while @webrick_thread.alive? && @server.status != :Running
raise "Couldn't start HTTPUNIXServer" unless @server.status == :Running
end
after(:all) do
@server.shutdown if @server
@webrick_thread.join if @webrick_thread
end
it "talks via HTTP ok" do
VCR.turned_off do
begin
WebMock.allow_net_connect!
http = Net::HTTPUNIX.new(tmp_socket_path)
expect(http.get('/').body).to eq('Hello World (at /)')
expect(http.get('/path').body).to eq('Hello World (at /path)')
ensure
WebMock.disable_net_connect!
end
end
end
end
require_relative 'spec_helper'
require_relative '../lib/object_dirs_helper'
describe ObjectDirsHelper do
before do
allow(Dir).to receive(:pwd).and_return('/home/git/repositories/foo/bar.git')
end
describe '.all_attributes' do
it do
expect(described_class.all_attributes.keys).to include(*%w[
GIT_OBJECT_DIRECTORY
GIT_OBJECT_DIRECTORY_RELATIVE
GIT_ALTERNATE_OBJECT_DIRECTORIES
GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE
])
end
end
describe '.absolute_object_dir' do
subject { described_class.absolute_object_dir }
context 'when GIT_OBJECT_DIRECTORY is set' do
let(:dir) { '/home/git/repositories/foo/bar.git/./objects' }
before do
allow(ENV).to receive(:[]).with('GIT_OBJECT_DIRECTORY').and_return(dir)
end
it { expect(subject).to eq(dir) }
end
context 'when GIT_OBJECT_DIRECTORY is not set' do
it { expect(subject).to be_nil }
end
end
describe '.absolute_alt_object_dirs' do
subject { described_class.absolute_alt_object_dirs }
context 'when GIT_ALTERNATE_OBJECT_DIRECTORIES is set' do
let(:dirs) { [
'/home/git/repositories/foo/bar.git/./incoming-UKU6Gl',
'/home/git/repositories/foo/bar.git/./incoming-AcU7Qr'
] }
before do
allow(ENV).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES').and_return(dirs.join(File::PATH_SEPARATOR))
end
it { expect(subject).to eq(dirs) }
end
context 'when GIT_ALTERNATE_OBJECT_DIRECTORIES is not set' do
it { expect(subject).to eq([]) }
end
end
describe '.relative_alt_object_dirs' do
subject { described_class.relative_alt_object_dirs }
context 'when GIT_ALTERNATE_OBJECT_DIRECTORIES is set' do
let(:dirs) { [
'/home/git/repositories/foo/bar.git/./objects/incoming-UKU6Gl',
'/home/git/repositories/foo/bar.git/./objects/incoming-AcU7Qr'
] }
before do
allow(ENV).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES').and_return(dirs.join(File::PATH_SEPARATOR))
end
it { expect(subject).to eq(['objects/incoming-UKU6Gl', 'objects/incoming-AcU7Qr']) }
end
context 'when GIT_ALTERNATE_OBJECT_DIRECTORIES is not set' do
it { expect(subject).to eq([]) }
end
end
describe '.relative_object_dir' do
subject { described_class.relative_object_dir }
context 'when GIT_OBJECT_DIRECTORY is set' do
before do
allow(ENV).to receive(:[]).with('GIT_OBJECT_DIRECTORY').and_return('/home/git/repositories/foo/bar.git/./objects')
end
it { expect(subject).to eq('objects') }
end
context 'when GIT_OBJECT_DIRECTORY is not set' do
it { expect(subject).to be_nil }
end
end
end
require 'pry' ROOT_PATH = File.expand_path('..', __dir__)
require 'rspec-parameterized'
require 'simplecov'
SimpleCov.start
require 'gitlab_init'
Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f } Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
RSpec.configure do |config| RSpec.configure do |config|
config.run_all_when_everything_filtered = true config.run_all_when_everything_filtered = true
config.filter_run :focus config.filter_run :focus
config.before(:each) do
stub_const('ROOT_PATH', File.expand_path('..', __dir__))
end
end end
require 'yaml'
RSpec.shared_context 'gitlab shell', shared_context: :metadata do RSpec.shared_context 'gitlab shell', shared_context: :metadata do
def original_root_path def original_root_path
ROOT_PATH ROOT_PATH
...@@ -45,9 +47,8 @@ RSpec.shared_context 'gitlab shell', shared_context: :metadata do ...@@ -45,9 +47,8 @@ RSpec.shared_context 'gitlab shell', shared_context: :metadata do
raise "Couldn't start stub GitlabNet server" unless @server.status == :Running raise "Couldn't start stub GitlabNet server" unless @server.status == :Running
system(original_root_path, 'bin/compile') system(original_root_path, 'bin/compile')
copy_dirs = ['bin', 'lib'] FileUtils.rm_rf(File.join(tmp_root_path, 'bin'))
FileUtils.rm_rf(copy_dirs.map { |d| File.join(tmp_root_path, d) }) FileUtils.cp_r('bin', tmp_root_path)
FileUtils.cp_r(copy_dirs, tmp_root_path)
end end
after(:all) do after(:all) do
......
#!/bin/sh
printenv GL_ID | grep -q '^key_1$'
#!/bin/bash
#echo "fail: $0"
exit 1
#!/bin/bash
#echo "ok: $0"
exit 0
require 'vcr'
VCR.configure do |c|
c.cassette_library_dir = 'spec/vcr_cassettes'
c.hook_into :webmock
c.configure_rspec_metadata!
end
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
---
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: 200
message: OK
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":true,"gl_repository":"project-3","gl_project_path":"gitlab-org/gitlab.test","repository_path":"/Users/dzaporozhets/Projects/gitlab-development-kit/repositories/gitlab-org/gitlab-test.git"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 404
message: Not Found
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- text/html
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: '<p>The project you were looking for could not be found.</p>'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 404
message: Not Found
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- text/plain
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: 'The project you were looking for could not be found.'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 404
message: Not Found
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":false,"message":"The project you were looking for could not be found."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- text/html
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: '<p>The project you were looking for could not be found.</p>'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- text/plain
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: 'The project you were looking for could not be found.'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
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":false,"message":"The project you were looking for could not be found."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
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,"gl_repository":"project-3","gl_project_path":"gitlab-org/gitlab.test","repository_path":"/Users/dzaporozhets/Projects/gitlab-development-kit/repositories/gitlab-org/gitlab-test.git"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/broadcast_message
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '2'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:50 GMT
Etag:
- W/"99914b932bd37a50b983c5e7c90ae93b"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- d31271ab-e21f-4349-a4c3-54f238c075c3
X-Runtime:
- '0.254031'
body:
encoding: UTF-8
string: "{}"
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:50 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/broadcast_message
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '153'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:29:13 GMT
Etag:
- W/"ce6de457fcc884f50125e81a10f165ce"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- ebaaf2c2-112e-4b3c-9182-e8714cd2a29c
X-Runtime:
- '0.276085'
body:
encoding: UTF-8
string: '{"message":"Message","starts_at":"2017-06-21T12:28:00.000Z","ends_at":"2017-06-21T12:35:00.000Z","color":"#e75e40","font":"#ffffff","id":1,"active":true}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:29:13 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/check
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '72'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:49 GMT
Etag:
- W/"e1b00c927355c31ffb83b0800821cdbd"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 19d8b75c-45d1-4b8b-9e47-2fda86f98187
X-Runtime:
- '0.245482'
body:
encoding: UTF-8
string: '{"api_version":"v4","gitlab_version":"9.3.0-pre","gitlab_rev":"8f537e5"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:49 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/fake/info_refs_bad
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-11"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea\n"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 403
message: Forbidden
headers:
Date:
- Fri, 20 Jul 2018 06:54:21 GMT
Connection:
- close
Content-Type:
- application/json
X-Request-Id:
- ea0644ac-e1ad-45f6-aa72-cc7910274318
X-Runtime:
- '1.236672'
body:
encoding: UTF-8
string: '{"message":"You cannot perform write operations on a read-only instance"}'
http_version:
recorded_at: Fri, 20 Jul 2018 06:54:21 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/fake/info_refs_bad
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-11"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea\n"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 403
message: Forbidden
headers:
Date:
- Fri, 20 Jul 2018 06:54:21 GMT
Connection:
- close
Content-Type:
- application/json
X-Request-Id:
- ea0644ac-e1ad-45f6-aa72-cc7910274318
X-Runtime:
- '1.236672'
body:
encoding: UTF-8
string: '""'
http_version:
recorded_at: Fri, 20 Jul 2018 06:54:21 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/fake/info_refs
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 200
message: OK
headers:
Date:
- Fri, 20 Jul 2018 06:18:58 GMT
Connection:
- close
X-Frame-Options:
- SAMEORIGIN
X-Content-Type-Options:
- nosniff
Content-Type:
- application/json
Content-Length:
- '172'
Vary:
- Origin
Etag:
- W/"7d01e1e3dbcbe7cca9607461352f8244"
Cache-Control:
- max-age=0, private, must-revalidate
X-Request-Id:
- 03afa234-b6be-49ab-9392-4aa35c5dee25
X-Runtime:
- '1.436040'
body:
encoding: UTF-8
string: '{'
http_version:
recorded_at: Fri, 20 Jul 2018 06:18:58 GMT
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/fake/info_refs
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 200
message: OK
headers:
Date:
- Fri, 20 Jul 2018 06:18:58 GMT
Connection:
- close
X-Frame-Options:
- SAMEORIGIN
X-Content-Type-Options:
- nosniff
Content-Type:
- application/json
Content-Length:
- '172'
Vary:
- Origin
Etag:
- W/"7d01e1e3dbcbe7cca9607461352f8244"
Cache-Control:
- max-age=0, private, must-revalidate
X-Request-Id:
- 03afa234-b6be-49ab-9392-4aa35c5dee25
X-Runtime:
- '1.436040'
body:
encoding: UTF-8
string: '{"result":"aW5mb19yZWZzLXJlc3VsdA==\n"}'
http_version:
recorded_at: Fri, 20 Jul 2018 06:18:58 GMT
- request:
method: post
uri: http://localhost:3000/api/v4/fake/push
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"info_refs-result","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 201
message: Created
headers:
Date:
- Fri, 20 Jul 2018 06:19:08 GMT
Connection:
- close
X-Frame-Options:
- SAMEORIGIN
X-Content-Type-Options:
- nosniff
Content-Type:
- application/json
Content-Length:
- '13'
Vary:
- Origin
Cache-Control:
- no-cache
X-Request-Id:
- 0c6894ac-7f8e-4cdb-871f-4cb64d3731ca
X-Runtime:
- '0.786754'
body:
encoding: UTF-8
string: '{"result":"cHVzaC1yZXN1bHQ=\n"}'
http_version:
recorded_at: Fri, 20 Jul 2018 06:19:08 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/fake/info_refs
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 200
message: OK
headers:
Date:
- Fri, 20 Jul 2018 06:18:58 GMT
Connection:
- close
X-Frame-Options:
- SAMEORIGIN
X-Content-Type-Options:
- nosniff
Content-Type:
- application/json
Content-Length:
- '172'
Vary:
- Origin
Etag:
- W/"7d01e1e3dbcbe7cca9607461352f8244"
Cache-Control:
- max-age=0, private, must-revalidate
X-Request-Id:
- 03afa234-b6be-49ab-9392-4aa35c5dee25
X-Runtime:
- '1.436040'
body:
encoding: UTF-8
string: '{"result":"aW5mb19yZWZzLXJlc3VsdA==\n"}'
http_version:
recorded_at: Fri, 20 Jul 2018 06:18:58 GMT
- request:
method: post
uri: http://localhost:3000/api/v4/fake/push
body:
encoding: UTF-8
string: '{"data":{"gl_username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git","gl_id":"key-1"},"output":"info_refs-result","secret_token":"0a3938d9d95d807e94d937af3a4fbbea"}'
headers:
Content-Type:
- application/json
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- localhost
response:
status:
code: 201
message: Created
headers:
Date:
- Fri, 20 Jul 2018 06:19:08 GMT
Connection:
- close
X-Frame-Options:
- SAMEORIGIN
X-Content-Type-Options:
- nosniff
Content-Type:
- application/json
Content-Length:
- '13'
Vary:
- Origin
Cache-Control:
- no-cache
X-Request-Id:
- 0c6894ac-7f8e-4cdb-871f-4cb64d3731ca
X-Runtime:
- '0.786754'
body:
encoding: UTF-8
string: '{"result":"cHVzaC1yZXN1bHQ=\n"}'
http_version:
recorded_at: Fri, 20 Jul 2018 06:19:08 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/discover?key_id=1
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '42'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:49 GMT
Etag:
- W/"63b4ab301951bea83c4fc398eba8e307"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- dc11b8d4-1972-417b-8305-2c35c849405c
X-Runtime:
- '0.230170'
body:
encoding: UTF-8
string: '{"name":"Administrator","username":"root"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:49 GMT
recorded_with: VCR 2.4.0
---
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=http&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '62'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:32:01 GMT
Etag:
- W/"71e09fcf8a60a03cd1acc22806386ead"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 70bdecc9-0078-4a4b-aa6b-cac1b2578886
X-Runtime:
- '0.324202'
body:
encoding: UTF-8
string: '{"status":false,"message":"Pulling over HTTP is not allowed."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:32:01 GMT
recorded_with: VCR 2.4.0
---
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=http&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '62'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:32:01 GMT
Etag:
- W/"7f14e23ac07cc8b0a53c567fcf9432fd"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 573f3584-87c6-41cb-a5bf-5e7ee76d4250
X-Runtime:
- '0.266135'
body:
encoding: UTF-8
string: '{"status":false,"message":"Pushing over HTTP is not allowed."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:32:01 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/lfs_authenticate
body:
encoding: US-ASCII
string: project=gitlab-org%2Fgitlab-test.git&key_id=1&operation=download&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '158'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:50 GMT
Etag:
- W/"0a8ccf1603566e521c169d5e43c86cd2"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- d82d6071-1868-4a37-b026-65ab37f96f2f
X-Runtime:
- '0.331056'
body:
encoding: UTF-8
string: '{"username":"root","lfs_token":"Hyzhyde_wLUeyUQsR3tHGTG8eNocVQm4ssioTEsBSdb6KwCSzQ","repository_http_path":"http://localhost:3000/gitlab-org/gitlab-test.git"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:50 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/lfs_authenticate
body:
encoding: US-ASCII
string: project=gitlab-org%2Fgitlab-test.git&key_id=1&operation=upload&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '158'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:50 GMT
Etag:
- W/"0a8ccf1603566e521c169d5e43c86cd2"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- d82d6071-1868-4a37-b026-65ab37f96f2f
X-Runtime:
- '0.331056'
body:
encoding: UTF-8
string: '{"username":"root","lfs_token":"Hyzhyde_wLUeyUQsR3tHGTG8eNocVQm4ssioTEsBSdb6KwCSzQ","repository_http_path":"http://localhost:3000/gitlab-org/gitlab-test.git"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:50 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/notify_post_receive
body:
encoding: US-ASCII
string: gl_repository=project-1&project=%2Fpath%2Fto%2Fmy%2Frepo.git&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '3'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:47:48 GMT
Etag:
- W/"3644a684f98ea8fe223c713b77189a77"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 407b184d-d6cf-43db-93fa-3f2d97112948
X-Runtime:
- '6.626186'
body:
encoding: UTF-8
string: '200'
http_version:
recorded_at: Wed, 21 Jun 2017 12:47:48 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/post_receive
body:
encoding: US-ASCII
string: gl_repository=project-1&identifier=key-1&changes=123456+789012+refs%2Fheads%2Ftest%0A654321+210987+refs%2Ftags%2Ftag&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: 404
message: Not Found
headers:
Cache-Control:
- no-cache
Content-Length:
- '25'
Content-Type:
- application/json
Date:
- Wed, 30 Aug 2017 22:24:37 GMT
Vary:
- Origin
X-Request-Id:
- bbfdb1ed-99dc-4246-a606-3074ffd5d87b
X-Runtime:
- '0.459681'
body:
encoding: UTF-8
string: '{"error":"404 Not Found"}'
http_version:
recorded_at: Wed, 30 Aug 2017 22:24:37 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/post_receive
body:
encoding: US-ASCII
string: gl_repository=project-1&identifier=key-1&changes=123456+789012+refs%2Fheads%2Ftest%0A654321+210987+refs%2Ftags%2Ftag&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '208'
Content-Type:
- application/json
Date:
- Wed, 30 Aug 2017 22:08:28 GMT
Etag:
- W/"1757d1411091b751684cde10119e0942"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- f7d422a7-c1a3-49d1-9b81-7fc48c954767
X-Runtime:
- '0.687283'
body:
encoding: UTF-8
string: '{"merge_request_urls":[{"branch_name":"test","url":"http://localhost:3000/gitlab-org/gitlab-test/merge_requests/7","new_merge_request":false}],"broadcast_message":"Message","reference_counter_decreased":true}'
http_version:
recorded_at: Wed, 30 Aug 2017 22:08:28 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/pre_receive
body:
encoding: US-ASCII
string: gl_repository=project-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: 404
message: Not Found
headers:
Cache-Control:
- no-cache
Content-Length:
- '25'
Content-Type:
- application/json
Date:
- Thu, 31 Aug 2017 16:41:13 GMT
Vary:
- Origin
X-Request-Id:
- 0b845e9a-5417-488d-bc5a-07d8c585b2da
X-Runtime:
- '0.295361'
body:
encoding: UTF-8
string: '{"error":"404 Not Found"}'
http_version:
recorded_at: Thu, 31 Aug 2017 16:41:13 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/pre_receive
body:
encoding: US-ASCII
string: gl_repository=project-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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '36'
Content-Type:
- application/json
Date:
- Thu, 31 Aug 2017 20:17:41 GMT
Etag:
- W/"7d4df85c493bd3d421351aa791a8fbf6"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- f0c84103-8dc0-48ea-a142-62554f6bca3d
X-Runtime:
- '0.612997'
body:
encoding: UTF-8
string: '{"reference_counter_increased":true}'
http_version:
recorded_at: Thu, 31 Aug 2017 20:17:41 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/authorized_keys?key=whatever
body:
encoding: US-ASCII
string: 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: 404
message: Not Found
headers:
Cache-Control:
- no-cache
Content-Length:
- '31'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:15:09 GMT
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c829f804-616e-4046-9b70-cc6a2a559166
X-Runtime:
- '0.281120'
body:
encoding: UTF-8
string: '{"message":"404 Key Not Found"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:15:09 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/authorized_keys?key=whatever
body:
encoding: US-ASCII
string: 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: 404
message: Not Found
headers:
Cache-Control:
- no-cache
Content-Length:
- '31'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:15:09 GMT
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 4bcc3073-f32d-4e07-994e-5eb476b23d41
X-Runtime:
- '0.448808'
body:
encoding: UTF-8
string: '{"message":"404 Key Not Found"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:15:09 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: get
uri: http://localhost:3000/api/v4/internal/authorized_keys?key=rsa-key
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '686'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:16:41 GMT
Etag:
- W/"18dfce1367306a2dda6aaf8b1e7795a8"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 839ab67c-7b72-44e0-916e-ca4f68c25604
X-Runtime:
- '9.070282'
body:
encoding: UTF-8
string: '{"id":99,"title":"untitled","key":"ssh-rsa rsa-key
dummy@gitlab.com","created_at":"2017-06-21T09:50:07.150Z","can_push":false}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:16:41 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:23:57 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 096ae253-c6fe-4360-b4d4-48f4b5435ca6
X-Runtime:
- '6.377187'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:23:57 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- text/html
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 8ce54f29-9ed0-46e5-aedb-37edaa3d52da
X-Runtime:
- '0.228256'
body:
encoding: UTF-8
string: '<p>Git access over SSH is not allowed</p>'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- text/plain
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 8ce54f29-9ed0-46e5-aedb-37edaa3d52da
X-Runtime:
- '0.228256'
body:
encoding: UTF-8
string: 'Git access over SSH is not allowed'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 8ce54f29-9ed0-46e5-aedb-37edaa3d52da
X-Runtime:
- '0.228256'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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&user_id=2&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:05 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 3b242d73-d860-48ac-8fef-80e2d0d3daca
X-Runtime:
- '0.342469'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:05 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 8ce54f29-9ed0-46e5-aedb-37edaa3d52da
X-Runtime:
- '0.228256'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:23:57 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 93620e06-fda9-4be5-855e-300f5d62fa3c
X-Runtime:
- '0.207159'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:23:57 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- text/html
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c843a5a3-fc08-46eb-aa45-caceae515638
X-Runtime:
- '7.359835'
body:
encoding: UTF-8
string: '<p>Git access over SSH is not allowed</p>'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- text/plain
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c843a5a3-fc08-46eb-aa45-caceae515638
X-Runtime:
- '7.359835'
body:
encoding: UTF-8
string: 'Git access over SSH is not allowed'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 401
message: Unauthorized
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c843a5a3-fc08-46eb-aa45-caceae515638
X-Runtime:
- '7.359835'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
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=2&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c843a5a3-fc08-46eb-aa45-caceae515638
X-Runtime:
- '7.359835'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/two_factor_recovery_codes
body:
encoding: US-ASCII
string: key_id=777&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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '58'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:33:42 GMT
Etag:
- W/"2642e3904bef8fa05ab8e0ca78c19047"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 7f8f2587-f6a2-445a-97a2-36fb9ea8f2d2
X-Runtime:
- '0.196409'
body:
encoding: UTF-8
string: '{"success":false,"message":"Could not find the given key"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:33:42 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/two_factor_recovery_codes
body:
encoding: US-ASCII
string: 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: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '225'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:31:42 GMT
Etag:
- W/"087adad20960d665d59e7e4ca02efd28"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- a0e1c950-83a7-4fed-afb0-bfb01d104795
X-Runtime:
- '1.378088'
body:
encoding: UTF-8
string: '{"success":true,"recovery_codes":["f67c514de60c4953","41278385fc00c1e0"]}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:31:42 GMT
recorded_with: VCR 2.4.0
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment