Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2019-01-17 19:43:15 +0300
committerJohn Cai <jcai@gitlab.com>2019-01-17 19:43:15 +0300
commitcb94c2b97427357f30678ce7164bb51250546d93 (patch)
tree2db93837ba5427ebe4b8256563e1935de64d5155
parentbe97ae4d2760e1065cbcd1fb66f37100b45699f0 (diff)
parent83a0c7b826280c7d9d09d0c5826a67c82cfd3016 (diff)
Merge branch 'vendor-gitlab-shell-20190111' into 'master'
Vendor gitlab-shell at 6c5b195353a632095d7f6 See merge request gitlab-org/gitaly!1037
-rw-r--r--Makefile4
-rw-r--r--_support/makegen.go10
-rw-r--r--changelogs/unreleased/vendor-gitlab-shell-20190111.yml5
-rw-r--r--ruby/.gitignore1
-rw-r--r--ruby/Gemfile6
-rw-r--r--ruby/Gemfile.lock26
-rw-r--r--ruby/vendor/gitlab-shell/.codeclimate.yml19
-rw-r--r--ruby/vendor/gitlab-shell/.gitignore18
-rw-r--r--ruby/vendor/gitlab-shell/.gitlab-ci.yml142
-rw-r--r--ruby/vendor/gitlab-shell/.rubocop.yml72
-rw-r--r--ruby/vendor/gitlab-shell/.ruby-version1
-rw-r--r--ruby/vendor/gitlab-shell/CHANGELOG481
-rw-r--r--ruby/vendor/gitlab-shell/CONTRIBUTING.md51
-rw-r--r--ruby/vendor/gitlab-shell/Gemfile11
-rw-r--r--ruby/vendor/gitlab-shell/Gemfile.lock104
-rw-r--r--ruby/vendor/gitlab-shell/LICENSE25
-rw-r--r--ruby/vendor/gitlab-shell/README.md5
-rw-r--r--ruby/vendor/gitlab-shell/README.orig.md131
-rw-r--r--ruby/vendor/gitlab-shell/VERSION1
-rwxr-xr-xruby/vendor/gitlab-shell/bin/authorized_keys25
-rwxr-xr-xruby/vendor/gitlab-shell/bin/check42
-rwxr-xr-xruby/vendor/gitlab-shell/bin/compile17
-rwxr-xr-xruby/vendor/gitlab-shell/bin/create-hooks46
-rwxr-xr-xruby/vendor/gitlab-shell/bin/gitlab-keys27
-rwxr-xr-xruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-keys-check42
-rwxr-xr-xruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-principals-check36
-rwxr-xr-xruby/vendor/gitlab-shell/bin/gitlab-shell-ruby28
-rwxr-xr-xruby/vendor/gitlab-shell/bin/install33
-rwxr-xr-xruby/vendor/gitlab-shell/bin/test-logger10
-rw-r--r--ruby/vendor/gitlab-shell/config.yml.example58
-rwxr-xr-xruby/vendor/gitlab-shell/hooks/post-receive22
-rwxr-xr-xruby/vendor/gitlab-shell/hooks/pre-receive32
-rwxr-xr-xruby/vendor/gitlab-shell/hooks/update18
-rw-r--r--ruby/vendor/gitlab-shell/lib/action.rb4
-rw-r--r--ruby/vendor/gitlab-shell/lib/action/custom.rb144
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_access.rb43
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_access_status.rb44
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_config.rb56
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_custom_hook.rb98
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_init.rb7
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_keys.rb190
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_lfs_authentication.rb30
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_logger.rb120
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_metrics.rb59
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_net.rb165
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_net/errors.rb4
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_post_receive.rb119
-rw-r--r--ruby/vendor/gitlab-shell/lib/gitlab_shell.rb270
-rw-r--r--ruby/vendor/gitlab-shell/lib/hooks_utils.rb15
-rw-r--r--ruby/vendor/gitlab-shell/lib/http_helper.rb125
-rw-r--r--ruby/vendor/gitlab-shell/lib/httpunix.rb54
-rw-r--r--ruby/vendor/gitlab-shell/lib/object_dirs_helper.rb39
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_access_spec.rb75
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_config_spec.rb40
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_custom_hook_spec.rb295
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_keys_spec.rb372
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_lfs_authentication_spec.rb37
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_logger_spec.rb145
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_metrics_spec.rb40
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_net_spec.rb554
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_post_receive_spec.rb270
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_shell_authorized_keys_check_spec.rb117
-rw-r--r--ruby/vendor/gitlab-shell/spec/gitlab_shell_spec.rb477
-rw-r--r--ruby/vendor/gitlab-shell/spec/hooks_utils_spec.rb28
-rw-r--r--ruby/vendor/gitlab-shell/spec/httpunix_spec.rb58
-rw-r--r--ruby/vendor/gitlab-shell/spec/object_dirs_helper_spec.rb95
-rw-r--r--ruby/vendor/gitlab-shell/spec/spec_helper.rb16
-rwxr-xr-xruby/vendor/gitlab-shell/spec/support/gl_id_test_hook2
-rwxr-xr-xruby/vendor/gitlab-shell/spec/support/hook_fail3
-rwxr-xr-xruby/vendor/gitlab-shell/spec/support/hook_ok3
-rw-r--r--ruby/vendor/gitlab-shell/spec/support/http_unix_server.rb35
-rw-r--r--ruby/vendor/gitlab-shell/spec/support/vcr.rb7
-rw-r--r--ruby/vendor/gitlab-shell/spec/support/webmock.rb3
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-pull.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-html.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-plain.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-html.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-plain.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-none.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-ok.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/check-ok.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-json.yml40
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-not-json.yml40
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-not-json.yml51
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-with-message.yml99
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok.yml99
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/discover-ok.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-pull-disabled.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-push-disabled.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-download.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-upload.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/notify-post-receive.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive-not-found.yml42
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive-not-found.yml42
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-found.yml44
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-implemented.yml44
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-ok.yml47
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-disabled.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-html.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-plain.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-with-user.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-disabled.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-html.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-plain.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes-fail.yml46
-rw-r--r--ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes.yml46
115 files changed, 7825 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index a19054faa..5e79ab055 100644
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,10 @@ test: prepare-build
rspec: prepare-build
cd $(BUILD_DIR) && $(MAKE) $@
+.PHONY: rspec-gitlab-shell
+rspec-gitlab-shell: prepare-build
+ cd $(BUILD_DIR) && $(MAKE) $@
+
.PHONY: verify
verify: prepare-build
cd $(BUILD_DIR) && $(MAKE) $@
diff --git a/_support/makegen.go b/_support/makegen.go
index 575bf7104..3200f3d25 100644
--- a/_support/makegen.go
+++ b/_support/makegen.go
@@ -310,7 +310,7 @@ binaries: assemble
prepare-tests: {{ .TestRepo }} {{ .GitTestRepo }} ../.ruby-bundle
.PHONY: test
-test: test-go rspec
+test: test-go rspec rspec-gitlab-shell
.PHONY: test-go
test-go: prepare-tests
@@ -324,6 +324,14 @@ race-go: prepare-tests
rspec: assemble-go prepare-tests
cd {{ .GitalyRubyDir }} && bundle exec rspec
+.PHONY: rspec-gitlab-shell
+rspec-gitlab-shell: {{ .GitlabShellDir }}/config.yml assemble-go prepare-tests
+ # rspec in {{ .GitlabShellRelDir }}
+ @cd {{ .GitalyRubyDir }} && bundle exec bin/ruby-cd {{ .GitlabShellDir }} rspec
+
+{{ .GitlabShellDir }}/config.yml: {{ .GitlabShellDir }}/config.yml.example
+ cp $< $@
+
.PHONY: verify
verify: lint check-formatting staticcheck govendor-status notice-up-to-date govendor-tagged rubocop
diff --git a/changelogs/unreleased/vendor-gitlab-shell-20190111.yml b/changelogs/unreleased/vendor-gitlab-shell-20190111.yml
new file mode 100644
index 000000000..208ae4919
--- /dev/null
+++ b/changelogs/unreleased/vendor-gitlab-shell-20190111.yml
@@ -0,0 +1,5 @@
+---
+title: Vendor gitlab-shell at 6c5b195353a632095d7f6
+merge_request: 1037
+author:
+type: other
diff --git a/ruby/.gitignore b/ruby/.gitignore
new file mode 100644
index 000000000..3b6ee585f
--- /dev/null
+++ b/ruby/.gitignore
@@ -0,0 +1 @@
+/coverage
diff --git a/ruby/Gemfile b/ruby/Gemfile
index e17ac114a..690195249 100644
--- a/ruby/Gemfile
+++ b/ruby/Gemfile
@@ -27,4 +27,10 @@ group :development, :test do
gem 'rspec-parameterized', require: false
gem 'timecop', require: false
gem 'factory_bot', require: false
+
+ # gitlab-shell spec gems
+ gem 'listen', '~> 0.5.0'
+ gem 'simplecov', '~> 0.9.0', require: false
+ gem 'vcr', '~> 4.0.0'
+ gem 'webmock', '~> 3.4.0'
end
diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock
index 7418309d7..4132df771 100644
--- a/ruby/Gemfile.lock
+++ b/ruby/Gemfile.lock
@@ -10,6 +10,8 @@ GEM
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)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
@@ -19,9 +21,12 @@ GEM
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
concurrent-ruby (1.1.3)
+ crack (0.4.3)
+ safe_yaml (~> 1.0.0)
crass (1.0.4)
debug_inspector (0.0.3)
diff-lcs (1.3)
+ docile (1.1.5)
equalizer (0.0.11)
escape_utils (1.2.1)
factory_bot (4.11.1)
@@ -63,12 +68,14 @@ GEM
grpc (1.15.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
+ hashdiff (0.3.8)
i18n (1.1.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
json (2.1.0)
licensee (8.9.2)
rugged (~> 0.24)
+ listen (0.5.3)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
mime-types (3.2.2)
@@ -76,6 +83,7 @@ GEM
mime-types-data (3.2018.0812)
mini_portile2 (2.3.0)
minitest (5.11.3)
+ multi_json (1.13.1)
multipart-post (2.0.0)
nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
@@ -91,6 +99,7 @@ GEM
parser
unparser
procto (0.0.3)
+ public_suffix (3.0.3)
rainbow (3.0.0)
rdoc (4.3.0)
rouge (3.3.0)
@@ -122,12 +131,18 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.10.0)
rugged (0.27.5)
+ safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4)
sentry-raven (2.7.4)
faraday (>= 0.7.6, < 1.0)
+ simplecov (0.9.2)
+ docile (~> 1.1.0)
+ multi_json (~> 1.0)
+ simplecov-html (~> 0.9.0)
+ simplecov-html (0.9.0)
stringex (2.8.4)
thread_safe (0.3.6)
timecop (0.9.1)
@@ -142,6 +157,11 @@ GEM
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
ruby
@@ -159,13 +179,17 @@ DEPENDENCIES
google-protobuf (~> 3.6)
grpc (~> 1.15.0)
licensee (~> 8.9.0)
+ listen (~> 0.5.0)
rdoc (~> 4.2)
rspec
rspec-parameterized
rubocop (~> 0.50)
rugged (~> 0.27)
sentry-raven (~> 2.7.2)
+ simplecov (~> 0.9.0)
timecop
+ vcr (~> 4.0.0)
+ webmock (~> 3.4.0)
BUNDLED WITH
- 1.17.1
+ 1.17.2
diff --git a/ruby/vendor/gitlab-shell/.codeclimate.yml b/ruby/vendor/gitlab-shell/.codeclimate.yml
new file mode 100644
index 000000000..172b2e384
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/.codeclimate.yml
@@ -0,0 +1,19 @@
+---
+engines:
+ bundler-audit:
+ enabled: true
+ duplication:
+ enabled: true
+ config:
+ languages:
+ - ruby
+ fixme:
+ enabled: true
+ rubocop:
+ enabled: true
+exclude_paths:
+- spec/
+- lib/vendor/
+- go/vendor/
+- tmp/
+- coverage/
diff --git a/ruby/vendor/gitlab-shell/.gitignore b/ruby/vendor/gitlab-shell/.gitignore
new file mode 100644
index 000000000..4ea71fb3f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/.gitignore
@@ -0,0 +1,18 @@
+config.yml
+tmp/*
+.idea
+*.log
+/*.log*
+authorized_keys.lock
+coverage/
+.gitlab_shell_secret
+.bundle
+tags
+.bundle/
+custom_hooks
+hooks/*.d
+/go_build
+/bin/gitlab-shell
+/bin/gitaly-upload-pack
+/bin/gitaly-receive-pack
+/bin/gitaly-upload-archive
diff --git a/ruby/vendor/gitlab-shell/.gitlab-ci.yml b/ruby/vendor/gitlab-shell/.gitlab-ci.yml
new file mode 100644
index 000000000..72b886427
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/.gitlab-ci.yml
@@ -0,0 +1,142 @@
+image: "ruby:2.3"
+
+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
+ - cp bin/gitlab-shell-ruby bin/gitlab-shell
+ - bundle exec rspec --color --tag ~go --format d spec
+
+rspec:
+ <<: *rspec_definition
+ tags:
+ - ruby
+ except:
+ - tags
+
+rubocop:
+ script:
+ - bundle exec rubocop
+ tags:
+ - ruby
+ except:
+ - tags
+
+#ruby 2.2
+rspec:ruby2.2:
+ image: ruby:2.2
+ variables:
+ INSTALL_BUNDLER_VERSION: ~> 1.17.3
+ <<: *rspec_definition
+ tags:
+ - ruby
+ except:
+ - tags
+
+#ruby 2.1
+rspec:ruby2.1:
+ image: ruby:2.1
+ variables:
+ INSTALL_BUNDLER_VERSION: ~> 1.17.3
+ <<: *rspec_definition
+ tags:
+ - ruby
+ except:
+ - tags
+
+.go: &go_definition
+ before_script:
+ - apt-get update -qq && apt-get install -y ruby ruby-dev
+ - ruby -v
+ - export PATH=~/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
+ - gem install --bindir /usr/local/bin bundler
+ - cp config.yml.example config.yml
+ - bundle install
+ script:
+ - go version
+ - which go
+ - bin/compile
+ - support/go-test
+ - support/go-format check
+ # Run the full Ruby test suite in the "go" tests. As more functionality is
+ # migrated into these tests and out of Ruby, the amount of work here will
+ # reduce
+ - bundle exec rspec --color --format d spec
+
+go:1.9:
+ <<: *go_definition
+ image: golang:1.9
+
+go:1.10:
+ <<: *go_definition
+ image: golang:1.10
+
+go:1.11:
+ <<: *go_definition
+ image: golang:1.10
+
+codequality:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ before_script: []
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
+ artifacts:
+ paths: [codeclimate.json]
+
+
+sast:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ before_script: []
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
+ --volume "$PWD:/code"
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
+ artifacts:
+ paths: [gl-sast-report.json]
+
+dependency_scanning:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ before_script: []
+ script:
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
+ --volume "$PWD:/code"
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
+ artifacts:
+ paths: [gl-dependency-scanning-report.json]
diff --git a/ruby/vendor/gitlab-shell/.rubocop.yml b/ruby/vendor/gitlab-shell/.rubocop.yml
new file mode 100644
index 000000000..66b795173
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/.rubocop.yml
@@ -0,0 +1,72 @@
+# Exclude some of GitLab files
+AllCops:
+ Exclude:
+ - 'spec/**/*'
+ - 'vendor/**/*'
+ - 'tmp/**/*'
+ - 'bin/**/*'
+ - 'hooks/**/*'
+ - 'Guardfile'
+
+Layout/DotPosition:
+ Enabled: false
+
+Lint/AmbiguousBlockAssociation:
+ Enabled: false
+
+Metrics/LineLength:
+ Enabled: false
+
+Metrics/MethodLength:
+ Enabled: false
+
+Metrics/BlockLength:
+ Enabled: false
+
+Metrics/ParameterLists:
+ Enabled: false
+
+Metrics/AbcSize:
+ Enabled: false
+
+Metrics/CyclomaticComplexity:
+ Enabled: false
+
+Metrics/PerceivedComplexity:
+ Enabled: false
+
+Style/Documentation:
+ Enabled: false
+
+Style/StringLiterals:
+ Enabled: false
+
+Style/StringLiterals:
+ Enabled: false
+
+Style/GlobalVars:
+ Enabled: false
+
+Style/AccessorMethodName:
+ Enabled: false
+
+Style/GuardClause:
+ Enabled: false
+
+Style/RescueModifier:
+ Enabled: false
+
+Style/PercentLiteralDelimiters:
+ Enabled: false
+
+Style/IfUnlessModifier:
+ Enabled: false
+
+Style/RegexpLiteral:
+ Enabled: false
+
+Style/SpecialGlobalVars:
+ Enabled: false
+
+Style/FrozenStringLiteralComment:
+ Enabled: false
diff --git a/ruby/vendor/gitlab-shell/.ruby-version b/ruby/vendor/gitlab-shell/.ruby-version
new file mode 100644
index 000000000..00355e29d
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/.ruby-version
@@ -0,0 +1 @@
+2.3.7
diff --git a/ruby/vendor/gitlab-shell/CHANGELOG b/ruby/vendor/gitlab-shell/CHANGELOG
new file mode 100644
index 000000000..592e99d5f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/CHANGELOG
@@ -0,0 +1,481 @@
+v8.4.4
+ - Pass push options along to gitlab-rails's post-receive endpoint
+
+v8.4.3
+ - Remove circular dependency between HTTPHelper and GitlabNet !258
+
+v8.4.2
+ - Include LFS operation when requesting auth !254
+
+v8.4.1
+ - Surface error message sent along with API Service Unavailable error to user
+
+v8.4.0
+ - Use Gitaly v2 auth scheme
+
+v8.3.3
+ - Release v8.3.3 as v8.3.2 tag is incorrect
+
+v8.3.2
+ - Ensure text/plain & text/html content types are handled !239
+ - Fix newlines not appearing between new log entries !242
+
+v8.3.1
+ - No changes (version tag correction)
+
+v8.3.0
+ - Add custom action (e.g. proxying SSH push) support
+
+v8.2.1
+ - Fix HTTP status code handling for /api/v4/allowed API endpoint
+
+v8.2.0
+ - Pass custom git_config_options to Gitaly !221
+ - Add missing require statement in create-hooks !225
+
+v8.1.1
+ - Fix two regressions in SSH certificate support (!226)
+
+v8.1.0
+ - Support Git v2 protocol (!217)
+
+v8.0.0
+ - SSH certificate support (!207)
+
+v7.2.0
+ - Update gitaly-proto to 0.109.0 (!216)
+
+v7.1.5
+ - Fix a NoMethodError in the pre-receive hook (!206)
+
+v7.1.4
+ - Don't truncate long strings in broadcast message (!202)
+
+v7.1.3
+ - Use username instead of full name for identifying users (!204)
+
+v7.1.2
+ - Add missing GitlabLogger#error method (!200)
+
+v7.1.1
+ - Flush log file after every write (!199)
+
+v7.1.0
+ - Migrate `git-upload-archive` to gitaly
+
+v7.0.0
+ - Switch to structured logging (!193)
+
+v6.0.4
+ - Don't delete GL_REPOSITORY environment variable from post-receive hook (!191)
+
+v6.0.3
+ - Print new project information in post-receive
+
+v6.0.2
+ - Use grpc-go 1.9.1 (!184)
+ - Update gitaly-proto and gitaly libs (!185)
+
+v6.0.1
+ - Fix git push by removing a bad require in the pre-receive hook (!183)
+
+v6.0.0
+ - Remove bin/gitlab_projects (!180)
+ - Remove direct redis integration (!181)
+ - Remove support unhiding of all references for Geo nodes (!179)
+
+v5.11.0
+ - Introduce a more-complete implementation of bin/authorized_keys (!178)
+
+v5.10.3
+ - Remove unused redis bin configuration
+
+v5.10.2
+ - Print redirection message when pushing into renamed project
+
+v5.10.1
+ - Use 'git clone --no-local' when creating a fork (!176)
+
+v5.10.0
+ - Add a 'fork-repository' command that works with hashed storage (!174)
+
+v5.9.4
+ - Add relative git object dir envvars to check access request
+
+v5.9.3
+ - Expose GitLab username to hooks in `GL_USERNAME` environment variable
+
+v5.9.2
+ - Fix pre-receive error when gitlab doesn't have /internal/pre_receive (!169)
+
+v5.9.1
+ - Adds --force option to push branches
+
+v5.9.0
+ - Support new /internal/pre-receive API endpoint for post-receive operations
+ - Support new /internal/post-receive API endpoint for post-receive operations
+ - Support `redis` field on /internal/check API endpoint
+
+v5.8.1
+ - Support old versions of ruby without monotonic clock
+
+v5.8.0
+ - Fix SSH support for Git for Windows v2.14
+
+v5.7.0
+ - Support unhiding of all refs via Gitaly
+
+v5.6.2
+ - Bump redis-rb library to 3.3.3
+
+v5.6.1
+ - Fix setting permissions of SSH key tempfiles
+ - Fix a missing constant error when using SSH authentication
+
+v5.6.0
+ - SSH authentication support
+
+v5.5.0
+ - Support unhiding of all references for Geo nodes
+
+v5.4.0
+ - Update Gitaly vendoring to use new RPC calls instead of old deprecated ones
+
+v5.3.1
+ - Gracefully handle internal API errors when getting merge request URLs
+
+v5.3.0
+ - Add ability to have git-receive-pack and git-upload-pack to go over Gitaly
+
+v5.2.1
+ - Revert changes in v5.2.0
+
+v5.2.0
+ - Disable RubyGems to increase performance
+
+v5.1.1
+ - Revert "Remove old `project` parameter, use `gl_repository` instead"
+
+v5.1.0
+ - Add `gitlab-keys list-key-ids` subcommand for iterating over key IDs to find keys that should be deleted
+
+v5.0.6
+ - Remove old `project` parameter, use `gl_repository` instead
+ - Use v4 of the GitLab REST API
+
+v5.0.5
+ - Use gl_repository if present when enqueing Sidekiq PostReceive jobs
+
+v5.0.4
+ - Handle GL_REPOSITORY env variable and use it in API calls and Sidekiq enqueuing
+
+v5.0.3
+ - Use recursive lookup for git repositories in the bin/create-hooks script
+
+v5.0.2
+ - Adds timeout option to push branches
+
+v5.0.1
+ - Fetch repositories with `--quiet` option by default
+
+v5.0.0
+ - Remove support for git-annex
+
+v4.1.1
+ - Send (a selection of) git environment variables while making the API call to `/allowed`, !112
+
+v4.1.0
+ - Add support for global custom hooks and chained hook directories (Elan Ruusamäe, Dirk Hörner), !113, !111, !93, !89, #32
+ - Clear up text with merge request after new branch push (Lisanne Fellinger)
+
+v4.0.3
+ - Fetch repositories with `--prune` option by default
+
+v4.0.2
+ - Fix gitlab_custom_hook dependencies
+
+v4.0.1
+ - Add instrumentation to push hooks
+
+v4.0.0
+ - Use full repository path for API calls
+
+v3.6.6
+ - Re-use the default logger when logging metrics data
+
+v3.6.5
+ - Test against ruby 2.3
+ - Instrument GitLab Shell and log metrics data to a file
+
+v3.6.4
+ - Fix rsync with ionice command building
+ - Fix short circuit logic between rsync with and without ionice for storage migrations
+
+v3.6.3
+ - Re-exposing GL_ID to custom hooks
+
+v3.6.2
+ - Enable GIT_TRACE/GIT_TRACE_PACKET/GIT_TRACE_PERFORMANCE by providing the git_trace_log_file config key
+
+v3.6.1
+ - Set a low IO priority for storage moves to lower performance impact
+
+v3.6.0
+ - Added full support for `git-lfs-authenticate` to properly handle LFS requests and pass them on to Workhorse
+
+v3.5.0
+ - Add option to recover 2FA via SSH
+
+v3.4.0
+ - Redis Sentinel support
+
+v3.3.3
+ - Print URL for new or existing merge request after push
+
+v3.3.2
+ - Improve authorized_keys check
+
+v3.3.1
+ - Manage authorized_keys permissions continuously
+
+v3.3.0
+ - Track ongoing push commands
+ - Add command to move repositories between repository storages
+
+v3.2.1
+ - Allow gitlab-project's fork-project command to fork projects between different repository storages
+
+v3.2.0
+ - Allow GitLab Shell to check for allowed access based on the used Git protocol
+ - Add an error message when using shell commands with incompatible GitLab versions
+
+v3.1.0
+ - Refactor repository paths handling to allow multiple git mount points
+
+v3.0.1
+ - Update PostReceive worker to provide enqueued_at time.
+
+v3.0.0
+ - Remove rm-tag command (Robert Schilling)
+ - Remove create-branch and rm-branch commands (Robert Schilling)
+ - Update PostReceive worker so it logs a unique JID in Sidekiq
+ - Remove update-head command
+ - Use Redis Ruby client instead of shelling out to redis-cli
+
+v2.7.2
+ - Do not prune objects during 'git gc'
+
+v2.7.1
+ - Add new command to list tags from a remote repo
+ - Add the ability to fetch remote repo with or without tags
+
+v2.7.0
+ - Add support for ssh AuthorizedKeysCommand query by key
+
+v2.6.13
+ - Add push-branches command
+ - Add delete-remote-branches command
+
+v2.6.12
+ - Fix git-annex issue not working using custom SSH port repositories
+
+v2.6.11
+ - Increase HTTP timeout and log request durations
+ - Workaround for a Webrick issue on Ruby 2.2
+ - New optional `--force` parameter for `gitlab-projects fetch-remote`
+
+v2.6.10
+ - Add git gc for housekeeping
+
+v2.6.9
+ - Remove trailing slashes from gitlab_url
+
+v2.6.8
+ - Revert git-lfs-authenticate command from white list
+
+v2.6.7
+ - Exit with non-zero status when import-repository fails
+ - Add fetch-remote command
+
+v2.6.6
+ - Do not clean LANG environment variable for the git hooks when working through the SSH-protocol
+ - Add git-lfs-authenticate command to white list (this command is used by git-lfs for SSO authentication through SSH-protocol)
+ - Handle git-annex and gcryptsetup
+
+v2.6.5
+ - Handle broken symlinks in create-hooks
+
+v2.6.4
+ - Remove keys from authorized_keys in-place
+ - Increase batch_add_keys lock timeout to 300 seconds
+ - If git-annex is enabled set GIT_ANNEX_SHELL_LIMITED variable
+
+v2.6.3
+ - Prevent keys with a very specific comment from accidentally being deleted.
+
+v2.6.2
+ - Include ecdsa keys in `gitlab_keys list-keys`.
+ - Refactor logic around GL_ID
+
+v2.6.1
+ - Write errors to stderr to get git to abort and show them as such.
+
+v2.6.0
+ - Prevent character encoding issues by sending received changes as raw data.
+
+v2.5.4
+ - Remove recursive commands from bin/install
+
+v2.5.3
+ - Improve git-annex integration
+
+v2.5.2
+ - Safer line sub for git-annex command
+
+v2.5.1
+ - Expect broadcast message to return empty JSON if no message now
+
+v2.5.0
+ - Support git-annex tool (disabled by default)
+ - Add rubocop (Ruby static code analyzer) for development
+
+v2.4.3
+ - Print broadcast message if one is available
+
+v2.4.2
+ - Pass git changes list as string instead of array
+
+v2.4.1
+ - Access token masking in url before loging
+
+v2.4.0
+ - Show error message when git push is rejected
+
+v2.2.0
+ - Support for custom hooks (Drew Blessing and Jose Kahan)
+
+v2.1.0
+ - Use secret token with GitLab internal API. Requires GitLab 7.5 or higher
+
+v2.0.1
+ - Send post-receive changes to redis as a string instead of array
+
+v2.0.0
+ - Works with GitLab v7.3+
+ - Replace raise with abort when checking path to prevent path exposure
+ - Handle invalid number of arguments on remote commands
+ - Replace update hook with pre-receive and post-receive hooks.
+ - Symlink the whole hooks directory
+ - Ignore missing repositories in create-hooks
+ - Connect to Redis via sockets by default
+
+v1.9.7
+ - Increased test coverage
+ - By default use direct unicorn connection (localhost:8080)
+ - Fix wrong repo path send to GitLab by GitlabUpdate hook
+
+v1.9.6
+ - Explicitly require 'timeout' from the standard library
+
+v1.9.5
+ - Put authorized_keys.lock in the same directory as authorized_keys
+ - Use lock file when add new entries to authorized_keys
+
+v1.9.4
+ - Use lock file when modify authorized_keys
+
+v1.9.3
+ - Ignore force push detection for new branch or branch remove push
+
+v1.9.2
+ - Add support for force push detection
+
+v1.9.1
+ - Update hook sends branch and tag name
+
+v1.9.0
+ - Call api in update hook for both ssdh and http push. Requires GitLab 6.7+
+ - Pass oldrev and newrev to api.allowed?
+
+v1.8.5
+ - Add `gitlab-keys batch-add-keys` subcommand for authorized_keys rebuilds
+
+v1.8.4
+ - Dont do import if repository exists
+
+v1.8.3
+ - Add timeout option for repository import
+
+v1.8.2
+ - Fix broken 1.8.1
+
+v1.8.1
+ - Restrict Environment Variables
+ - Add bin/create-hooks command
+ - More safe shell execution
+
+v1.8.0
+ - Fix return values in GitlabKeys
+
+v1.7.9
+ - Fix escape of repository path for custom ssh port
+
+v1.7.8
+ - Escape repository path to prevent relative links (CVE-2013-4583)
+
+v1.7.7
+ - Separate options from arguments with -- (CVE-2013-4582)
+ - Bypass shell and use stdlib JSON for GitlabUpdate (CVE-2013-4581)
+
+v1.7.6
+ - Fix gitlab-projects update-head for improted repo when branch exists but not listed in refs/head
+
+v1.7.5
+ - Remove keys from authorized_keys using ruby instead of shell
+
+v1.7.4
+ - More protection against shell injection (CVE-2013-4546)
+
+v1.7.3
+ - Use Kernel#open to append lines to authorized_keys (CVE-2013-4490)
+
+v1.7.2
+ - More safe command execution
+
+v1.7.1
+ - Fixed issue when developers are able to push to protected branches that contain a '/' in the branch name.
+
+v1.7.0
+ - Clean authorized_keys file with `gitlab-keys clear`
+
+v1.6.0
+ - Create branch/tag functionality
+ - Remove branch/tag functionality
+
+v1.5.0
+ - Logger
+ - Ability to specify ca_file/ca_path
+ - Update-head command for project
+ - Better regexp for key_id inside shell
+
+v1.4.0
+ - Regex used in rm-key command was too lax
+
+v1.3.0
+ - Fork-project command
+ - Custom redis configuration
+ - Interpret login with deploy key as anonymous one
+
+v1.2.0
+ - Return non-zero result if gitlab-projects and gitlab-keys execution was not successful
+ - http_settings configuration option added
+
+v1.1.0
+ - added mv-project feature
+ - increased test coverage
+
+v1.0.4
+ - requires gitlab c9ca15e
+ - don't use post-receive file any more. Make all updates in update
+ - fixed issue with invalid GL_USER
+ - use GL_ID instead of GL_USER
diff --git a/ruby/vendor/gitlab-shell/CONTRIBUTING.md b/ruby/vendor/gitlab-shell/CONTRIBUTING.md
new file mode 100644
index 000000000..32b7d2348
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/CONTRIBUTING.md
@@ -0,0 +1,51 @@
+## Contributing
+
+Thank you for your interest in contributing to this GitLab project! We welcome
+all contributions. By participating in this project, you agree to abide by the
+[code of conduct](#code-of-conduct).
+
+## Developer Certificate of Origin + License
+
+By contributing to GitLab B.V., You accept and agree to the following terms and
+conditions for Your present and future Contributions submitted to GitLab B.V.
+Except for the license granted herein to GitLab B.V. and recipients of software
+distributed by GitLab B.V., You reserve all right, title, and interest in and to
+Your Contributions. All Contributions are subject to the following DCO + License
+terms.
+
+[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
+
+_This notice should stay as the first item in the CONTRIBUTING.md file._
+
+## Code of conduct
+
+As contributors and maintainers of this project, we pledge to respect all people
+who contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual
+language or imagery, derogatory comments or personal attacks, trolling, public
+or private harassment, insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct. Project maintainers who do not follow the
+Code of Conduct may be removed from the project team.
+
+This code of conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior can be
+reported by emailing contact@gitlab.com.
+
+This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
+available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
+
+[contributor-covenant]: http://contributor-covenant.org
+[individual-agreement]: https://docs.gitlab.com/ee/legal/individual_contributor_license_agreement.html
+[corporate-agreement]: https://docs.gitlab.com/ee/legal/corporate_contributor_license_agreement.html
diff --git a/ruby/vendor/gitlab-shell/Gemfile b/ruby/vendor/gitlab-shell/Gemfile
new file mode 100644
index 000000000..4166d5071
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/Gemfile
@@ -0,0 +1,11 @@
+source 'https://rubygems.org'
+
+group :development, :test do
+ gem 'listen', '~> 0.5.0'
+ gem 'rspec', '~> 3.8.0'
+ gem 'rspec-parameterized', '~> 0.4.0'
+ 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
diff --git a/ruby/vendor/gitlab-shell/Gemfile.lock b/ruby/vendor/gitlab-shell/Gemfile.lock
new file mode 100644
index 000000000..f23d7817c
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/Gemfile.lock
@@ -0,0 +1,104 @@
+GEM
+ remote: https://rubygems.org/
+ 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)
+ 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)
+ 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)
+ multi_json (1.13.1)
+ parallel (1.12.1)
+ parser (2.5.1.2)
+ ast (~> 2.4.0)
+ powerpack (0.1.2)
+ proc_to_ast (0.1.0)
+ coderay
+ parser
+ unparser
+ procto (0.0.3)
+ public_suffix (3.0.3)
+ rainbow (2.2.2)
+ rake
+ rake (12.3.1)
+ rspec (3.8.0)
+ rspec-core (~> 3.8.0)
+ rspec-expectations (~> 3.8.0)
+ rspec-mocks (~> 3.8.0)
+ rspec-core (3.8.0)
+ rspec-support (~> 3.8.0)
+ rspec-expectations (3.8.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.8.0)
+ rspec-mocks (3.8.0)
+ diff-lcs (>= 1.2.0, < 2.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)
+ rubocop (0.49.1)
+ parallel (~> 1.10)
+ parser (>= 2.3.3.1, < 3.0)
+ powerpack (~> 0.1)
+ rainbow (>= 1.99.1, < 3.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 1.0, >= 1.0.1)
+ 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)
+ 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
+ ruby
+
+DEPENDENCIES
+ listen (~> 0.5.0)
+ rspec (~> 3.8.0)
+ rspec-parameterized (~> 0.4.0)
+ rubocop (= 0.49.1)
+ simplecov (~> 0.9.0)
+ vcr (~> 4.0.0)
+ webmock (~> 3.4.0)
+
+BUNDLED WITH
+ 1.16.3
diff --git a/ruby/vendor/gitlab-shell/LICENSE b/ruby/vendor/gitlab-shell/LICENSE
new file mode 100644
index 000000000..06c55d0ae
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2011-2018 GitLab B.V.
+
+With regard to the GitLab Software:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+For all third party components incorporated into the GitLab Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
diff --git a/ruby/vendor/gitlab-shell/README.md b/ruby/vendor/gitlab-shell/README.md
new file mode 100644
index 000000000..c562fc44c
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/README.md
@@ -0,0 +1,5 @@
+# gitlab-shell
+
+Vendored from https://gitlab.com/gitlab-org/gitlab-shell at [6c5b195353a632095d7f672d28b9985fd879b077](https://gitlab.com/gitlab-org/gitlab-shell/commit/6c5b195353a632095d7f672d28b9985fd879b077).
+
+Original README: [README.orig.md](README.orig.md).
diff --git a/ruby/vendor/gitlab-shell/README.orig.md b/ruby/vendor/gitlab-shell/README.orig.md
new file mode 100644
index 000000000..55f7c61e9
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/README.orig.md
@@ -0,0 +1,131 @@
+# GitLab Shell
+
+## GitLab Shell handles git SSH sessions for GitLab
+
+GitLab Shell handles git SSH sessions for GitLab and modifies the list of authorized keys.
+GitLab Shell is not a Unix shell nor a replacement for Bash or Zsh.
+
+When you access the GitLab server over SSH then GitLab Shell will:
+
+1. Limits you to predefined git commands (git push, git pull).
+1. Call the GitLab Rails API to check if you are authorized, and what Gitaly server your repository is on
+1. Copy data back and forth between the SSH client and the Gitaly server
+
+If you access a GitLab server over HTTP(S) you end up in [gitlab-workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse).
+
+An overview of the four cases described above:
+
+1. git pull over ssh -> gitlab-shell -> API call to gitlab-rails (Authorization) -> accept or decline -> establish Gitaly session
+1. git push over ssh -> gitlab-shell (git command is not executed yet) -> establish Gitaly session -> (in Gitaly) gitlab-shell pre-receive hook -> API call to gitlab-rails (authorization) -> accept or decline push
+
+## Git hooks
+
+For historical reasons the gitlab-shell repository also contains the
+Git hooks that allow GitLab to validate Git pushes (e.g. "is this user
+allowed to push to this protected branch"). These hooks also trigger
+events in GitLab (e.g. to start a CI pipeline after a push). In
+GitLab's current architecture (Q4 2018) these hooks belong to Gitaly
+more than gitlab-shell. We [are moving them to the Gitaly
+repository](https://gitlab.com/gitlab-org/gitaly/issues/1226).
+
+## Code status
+
+[![pipeline status](https://gitlab.com/gitlab-org/gitlab-shell/badges/master/pipeline.svg)](https://gitlab.com/gitlab-org/gitlab-shell/commits/master)
+[![coverage report](https://gitlab.com/gitlab-org/gitlab-shell/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-shell/commits/master)
+[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlab-shell.svg)](https://codeclimate.com/github/gitlabhq/gitlab-shell)
+
+## 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).**
+It requires ruby 2.0 or higher.
+Please uninstall any old ruby versions from your system:
+
+```
+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
+
+ ./bin/install
+ ./bin/compile
+
+## Check
+
+ ./bin/check
+
+## Keys
+
+Add key:
+
+ ./bin/gitlab-keys add-key key-782 "ssh-rsa AAAAx321..."
+
+Remove key:
+
+ ./bin/gitlab-keys rm-key key-23 "ssh-rsa AAAAx321..."
+
+List all keys:
+
+ ./bin/gitlab-keys list-keys
+
+
+Remove all keys from authorized_keys file:
+
+ ./bin/gitlab-keys clear
+
+## Git LFS remark
+
+Starting with GitLab 8.12, GitLab supports Git LFS authentication through ssh.
+
+## Releasing a new version
+
+GitLab Shell is versioned by git tags, and the version used by the Rails
+application is stored in
+[`GITLAB_SHELL_VERSION`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/GITLAB_SHELL_VERSION).
+
+For each version, there is a raw version and a tag version:
+
+- The **raw version** is the version number. For instance, `15.2.8`.
+- The **tag version** is the raw version prefixed with `v`. For instance, `v15.2.8`.
+
+To release a new version of GitLab Shell and have that version available to the
+Rails application:
+
+1. Update the [`CHANGELOG`](CHANGELOG) with the **tag version** and the
+ [`VERSION`](VERSION) file with the **raw version**.
+2. Add a new git tag with the **tag version**.
+3. Update `GITLAB_SHELL_VERSION` in the Rails application to the **raw
+ 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.)
+
+## 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
+
+See [CONTRIBUTING.md](./CONTRIBUTING.md).
+
+## License
+
+See [LICENSE](./LICENSE).
diff --git a/ruby/vendor/gitlab-shell/VERSION b/ruby/vendor/gitlab-shell/VERSION
new file mode 100644
index 000000000..2eb8a04cb
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/VERSION
@@ -0,0 +1 @@
+8.4.4 \ No newline at end of file
diff --git a/ruby/vendor/gitlab-shell/bin/authorized_keys b/ruby/vendor/gitlab-shell/bin/authorized_keys
new file mode 100755
index 000000000..ca0164607
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/authorized_keys
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+#
+# GitLab shell authorized_keys. Query GitLab API to get the authorized command for a given ssh key fingerprint
+#
+# Ex.
+# /bin/authorized_keys BASE64-KEY
+#
+# Returns
+# command="/bin/gitlab-shell key-#",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...
+#
+
+key = ARGV[0]
+abort "# No key provided" if key.nil? || key.empty?
+
+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
diff --git a/ruby/vendor/gitlab-shell/bin/check b/ruby/vendor/gitlab-shell/bin/check
new file mode 100755
index 000000000..d2224a641
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/check
@@ -0,0 +1,42 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/gitlab_init'
+require_relative '../lib/gitlab_net'
+
+#
+# GitLab shell check task
+#
+
+print "Check GitLab API access: "
+begin
+ resp = GitlabNet.new.check
+
+ if resp.code != "200"
+ abort "FAILED. code: #{resp.code}"
+ end
+
+ puts 'OK'
+
+ check_values = JSON.parse(resp.body)
+
+ print 'Redis available via internal API: '
+ if check_values['redis']
+ puts 'OK'
+ else
+ abort 'FAILED'
+ end
+rescue GitlabNet::ApiUnreachableError
+ abort "FAILED: Failed to connect to internal API"
+end
+
+config = GitlabConfig.new
+
+abort("ERROR: missing option in config.yml") unless config.auth_file
+
+print "\nAccess to #{config.auth_file}: "
+if system(File.dirname(__FILE__) + '/gitlab-keys', 'check-permissions')
+ print 'OK'
+else
+ abort "FAILED"
+end
+puts "\n"
diff --git a/ruby/vendor/gitlab-shell/bin/compile b/ruby/vendor/gitlab-shell/bin/compile
new file mode 100755
index 000000000..fc5fb6c65
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/compile
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+require 'fileutils'
+
+require_relative '../support/go_build'
+include GoBuild
+
+def main
+ create_fresh_build_dir
+
+ run!(GO_ENV, %W[go install #{GO_PACKAGE}/cmd/...])
+ executables = Dir[File.join(BUILD_DIR, 'bin', '*')]
+ FileUtils.chmod(0755, executables)
+ FileUtils.cp(executables, File.join(ROOT_PATH, 'bin'))
+end
+
+main
diff --git a/ruby/vendor/gitlab-shell/bin/create-hooks b/ruby/vendor/gitlab-shell/bin/create-hooks
new file mode 100755
index 000000000..1adc80984
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/create-hooks
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+
+# Recreate GitLab hooks in the Git repositories managed by GitLab.
+#
+# This script is used when restoring a GitLab backup.
+
+require_relative '../lib/gitlab_init'
+require File.join(ROOT_PATH, 'lib', 'gitlab_metrics')
+require 'fileutils'
+
+def create_hooks(path)
+ global_hooks_directory = File.join(ROOT_PATH, 'hooks')
+ local_hooks_directory = File.join(path, 'hooks')
+ real_local_hooks_directory = :not_found
+
+ begin
+ real_local_hooks_directory = File.realpath(local_hooks_directory)
+ rescue Errno::ENOENT
+ # real_local_hooks_directory == :not_found
+ end
+
+ if real_local_hooks_directory != File.realpath(global_hooks_directory)
+ if File.exist?(local_hooks_directory)
+ $logger.info "Moving existing hooks directory and symlinking global hooks directory for #{path}."
+ FileUtils.mv(local_hooks_directory, "#{local_hooks_directory}.old.#{Time.now.to_i}")
+ end
+ FileUtils.ln_sf(global_hooks_directory, local_hooks_directory)
+ else
+ $logger.info "Hooks already exist for #{path}."
+ true
+ end
+end
+
+repository_storage_paths = ARGV
+
+repository_storage_paths.each do |repo_path|
+ Dir["#{repo_path.chomp('/')}/**/*.git"].each do |repo|
+ begin
+ GitlabMetrics.measure('command-create-hooks') do
+ create_hooks(repo)
+ end
+ rescue Errno::ENOENT
+ # The user must have deleted their repository. Ignore.
+ end
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/bin/gitlab-keys b/ruby/vendor/gitlab-shell/bin/gitlab-keys
new file mode 100755
index 000000000..9eb19503e
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/gitlab-keys
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/gitlab_init'
+
+#
+# GitLab Keys shell. Add/remove keys from ~/.ssh/authorized_keys
+#
+# Ex.
+# /bin/gitlab-keys add-key key-782 "ssh-rsa AAAAx321..."
+#
+# printf "key-782\tssh-rsa AAAAx321...\n" | /bin/gitlab-keys batch-add-keys
+#
+# /bin/gitlab-keys rm-key key-23 "ssh-rsa AAAAx321..."
+#
+# /bin/gitlab-keys list-keys
+#
+# /bin/gitlab-keys clear
+#
+
+require File.join(ROOT_PATH, 'lib', 'gitlab_keys')
+
+# Return non-zero if command execution was not successful
+if GitlabKeys.new.exec
+ exit 0
+else
+ exit 1
+end
diff --git a/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-keys-check b/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-keys-check
new file mode 100755
index 000000000..2ea1a7488
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-keys-check
@@ -0,0 +1,42 @@
+#!/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
diff --git a/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-principals-check b/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-principals-check
new file mode 100755
index 000000000..aa6d427c3
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/gitlab-shell-authorized-principals-check
@@ -0,0 +1,36 @@
+#!/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)
+}
diff --git a/ruby/vendor/gitlab-shell/bin/gitlab-shell-ruby b/ruby/vendor/gitlab-shell/bin/gitlab-shell-ruby
new file mode 100755
index 000000000..8d43cf14a
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/gitlab-shell-ruby
@@ -0,0 +1,28 @@
+#!/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
diff --git a/ruby/vendor/gitlab-shell/bin/install b/ruby/vendor/gitlab-shell/bin/install
new file mode 100755
index 000000000..e9c165458
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/install
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/gitlab_init'
+
+#
+# GitLab shell, invoked from ~/.ssh/authorized_keys
+#
+
+config = GitlabConfig.new
+key_dir = File.dirname("#{config.auth_file}")
+repository_storage_paths = ARGV
+
+commands = [
+ %W(mkdir -p #{key_dir}),
+ %W(chmod 700 #{key_dir}),
+]
+
+repository_storage_paths.each do |repository_storage_path|
+ commands << %W(mkdir -p #{repository_storage_path})
+ commands << %W(chmod ug+rwX,o-rwx #{repository_storage_path})
+end
+
+commands.each do |cmd|
+ print "#{cmd.join(' ')}: "
+ if system(*cmd)
+ puts 'OK'
+ else
+ puts 'Failed'
+ abort "#{$PROGRAM_NAME} failed"
+ end
+end
+
+exit
diff --git a/ruby/vendor/gitlab-shell/bin/test-logger b/ruby/vendor/gitlab-shell/bin/test-logger
new file mode 100755
index 000000000..11b2a558a
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/bin/test-logger
@@ -0,0 +1,10 @@
+#!/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')
diff --git a/ruby/vendor/gitlab-shell/config.yml.example b/ruby/vendor/gitlab-shell/config.yml.example
new file mode 100644
index 000000000..3d3e58062
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/config.yml.example
@@ -0,0 +1,58 @@
+#
+# If you change this file in a Merge Request, please also create
+# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
+#
+
+# GitLab user. git by default
+user: git
+
+# URL to GitLab instance, used for API calls. Default: http://localhost:8080.
+# For relative URL support read http://doc.gitlab.com/ce/install/relative_url.html
+# You only have to change the default if you have configured Unicorn
+# to listen on a custom port, or if you have configured Unicorn to
+# only listen on a Unix domain socket. For Unix domain sockets use
+# "http+unix://<urlquoted-path-to-socket>", e.g.
+# "http+unix://%2Fpath%2Fto%2Fsocket"
+gitlab_url: "http://localhost:8080"
+
+# See installation.md#using-https for additional HTTPS configuration details.
+http_settings:
+# read_timeout: 300
+# user: someone
+# password: somepass
+# ca_file: /etc/ssl/cert.pem
+# ca_path: /etc/pki/tls/certs
+ self_signed_cert: false
+
+# File used as authorized_keys for gitlab user
+auth_file: "/home/git/.ssh/authorized_keys"
+
+# File that contains the secret key for verifying access to GitLab.
+# Default is .gitlab_shell_secret in the gitlab-shell directory.
+# secret_file: "/home/git/gitlab-shell/.gitlab_shell_secret"
+
+# Parent directory for global custom hook directories (pre-receive.d, update.d, post-receive.d)
+# Default is hooks in the gitlab-shell directory.
+# custom_hooks_dir: "/home/git/gitlab-shell/hooks"
+
+# Log file.
+# Default is gitlab-shell.log in the root directory.
+# log_file: "/home/git/gitlab-shell/gitlab-shell.log"
+
+# Log level. INFO by default
+log_level: INFO
+
+# Log format. 'text' by default
+# log_format: json
+
+# Audit usernames.
+# Set to true to see real usernames in the logs instead of key ids, which is easier to follow, but
+# incurs an extra API call on every gitlab-shell command.
+audit_usernames: false
+
+# Migration to Go: anything listed here has two implementations. Use these flags
+# to try the new implementations out, or to revert to the old behaviour if there
+# problems arise.
+migration:
+ enabled: false
+ features: []
diff --git a/ruby/vendor/gitlab-shell/hooks/post-receive b/ruby/vendor/gitlab-shell/hooks/post-receive
new file mode 100755
index 000000000..2b6538f03
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/hooks/post-receive
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+
+# This file was placed here by GitLab. It makes sure that your pushed commits
+# will be processed properly.
+
+refs = $stdin.read
+key_id = ENV.delete('GL_ID')
+gl_repository = ENV['GL_REPOSITORY']
+repo_path = Dir.pwd
+
+require_relative '../lib/gitlab_custom_hook'
+require_relative '../lib/hooks_utils'
+require_relative '../lib/gitlab_post_receive'
+
+push_options = HooksUtils.get_push_options
+
+if GitlabPostReceive.new(gl_repository, repo_path, key_id, refs, push_options).exec &&
+ GitlabCustomHook.new(repo_path, key_id).post_receive(refs)
+ exit 0
+else
+ exit 1
+end
diff --git a/ruby/vendor/gitlab-shell/hooks/pre-receive b/ruby/vendor/gitlab-shell/hooks/pre-receive
new file mode 100755
index 000000000..6ce587951
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/hooks/pre-receive
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+# This file was placed here by GitLab. It makes sure that your pushed commits
+# will be processed properly.
+
+refs = $stdin.read
+key_id = ENV.delete('GL_ID')
+protocol = ENV.delete('GL_PROTOCOL')
+repo_path = Dir.pwd
+gl_repository = ENV['GL_REPOSITORY']
+
+def increase_reference_counter(gl_repository, repo_path)
+ result = GitlabNet.new.pre_receive(gl_repository)
+
+ result && result['reference_counter_increased']
+end
+
+require_relative '../lib/gitlab_custom_hook'
+require_relative '../lib/gitlab_access'
+require_relative '../lib/gitlab_net'
+
+# It's important that on pre-receive `increase_reference_counter` gets executed
+# last so that it only runs if everything else succeeded. On post-receive on the
+# other hand, we run GitlabPostReceive first because the push is already done
+# and we don't want to skip it if the custom hook fails.
+if GitlabAccess.new(gl_repository, repo_path, key_id, refs, protocol).exec &&
+ GitlabCustomHook.new(repo_path, key_id).pre_receive(refs) &&
+ increase_reference_counter(gl_repository, repo_path)
+ exit 0
+else
+ exit 1
+end
diff --git a/ruby/vendor/gitlab-shell/hooks/update b/ruby/vendor/gitlab-shell/hooks/update
new file mode 100755
index 000000000..4c2fc08b0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/hooks/update
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+# This file was placed here by GitLab. It makes sure that your pushed commits
+# will be processed properly.
+
+ref_name = ARGV[0]
+old_value = ARGV[1]
+new_value = ARGV[2]
+repo_path = Dir.pwd
+key_id = ENV.delete('GL_ID')
+
+require_relative '../lib/gitlab_custom_hook'
+
+if GitlabCustomHook.new(repo_path, key_id).update(ref_name, old_value, new_value)
+ exit 0
+else
+ exit 1
+end
diff --git a/ruby/vendor/gitlab-shell/lib/action.rb b/ruby/vendor/gitlab-shell/lib/action.rb
new file mode 100644
index 000000000..28c1c146f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/action.rb
@@ -0,0 +1,4 @@
+require_relative 'action/custom'
+
+module Action
+end
diff --git a/ruby/vendor/gitlab-shell/lib/action/custom.rb b/ruby/vendor/gitlab-shell/lib/action/custom.rb
new file mode 100644
index 000000000..a2f3d59c8
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/action/custom.rb
@@ -0,0 +1,144 @@
+require 'base64'
+
+require_relative '../http_helper'
+
+module Action
+ class Custom
+ include HTTPHelper
+
+ 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)
+ str.split("\n").map { |line| "> GitLab: #{line}" }.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
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_access.rb b/ruby/vendor/gitlab-shell/lib/gitlab_access.rb
new file mode 100644
index 000000000..72abd14d9
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_access.rb
@@ -0,0 +1,43 @@
+require_relative 'gitlab_init'
+require_relative 'gitlab_net'
+require_relative 'gitlab_access_status'
+require_relative 'gitlab_metrics'
+require_relative 'object_dirs_helper'
+require 'json'
+
+class GitlabAccess
+ class AccessDeniedError < StandardError; end
+
+ attr_reader :config, :gl_repository, :repo_path, :changes, :protocol
+
+ def initialize(gl_repository, repo_path, actor, changes, protocol)
+ @config = GitlabConfig.new
+ @gl_repository = gl_repository
+ @repo_path = repo_path.strip
+ @actor = actor
+ @changes = changes.lines
+ @protocol = protocol
+ end
+
+ def exec
+ status = GitlabMetrics.measure('check-access:git-receive-pack') do
+ api.check_access('git-receive-pack', @gl_repository, @repo_path, @actor, @changes, @protocol, env: ObjectDirsHelper.all_attributes.to_json)
+ end
+
+ raise AccessDeniedError, status.message unless status.allowed?
+
+ true
+ rescue GitlabNet::ApiUnreachableError
+ $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable"
+ false
+ rescue AccessDeniedError => ex
+ $stderr.puts "GitLab: #{ex.message}"
+ false
+ end
+
+ protected
+
+ def api
+ GitlabNet.new
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_access_status.rb b/ruby/vendor/gitlab-shell/lib/gitlab_access_status.rb
new file mode 100644
index 000000000..dd6562ece
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_access_status.rb
@@ -0,0 +1,44 @@
+require 'json'
+
+class GitAccessStatus
+ HTTP_MULTIPLE_CHOICES = '300'.freeze
+
+ attr_reader :message, :gl_repository, :gl_id, :gl_username, :gitaly, :git_protocol, :git_config_options, :payload
+
+ def initialize(status, status_code, message, gl_repository: nil, gl_id: nil,
+ gl_username: nil, gitaly: nil, git_protocol: nil,
+ git_config_options: nil, payload: nil)
+ @status = status
+ @status_code = status_code
+ @message = message
+ @gl_repository = gl_repository
+ @gl_id = gl_id
+ @gl_username = gl_username
+ @git_config_options = git_config_options
+ @gitaly = gitaly
+ @git_protocol = git_protocol
+ @payload = payload
+ 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_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"])
+ end
+
+ def allowed?
+ @status
+ end
+
+ def custom_action?
+ @status_code == HTTP_MULTIPLE_CHOICES
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_config.rb b/ruby/vendor/gitlab-shell/lib/gitlab_config.rb
new file mode 100644
index 000000000..85aa88991
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_config.rb
@@ -0,0 +1,56 @@
+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
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_custom_hook.rb b/ruby/vendor/gitlab-shell/lib/gitlab_custom_hook.rb
new file mode 100644
index 000000000..67096dfe7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_custom_hook.rb
@@ -0,0 +1,98 @@
+require 'open3'
+require_relative 'gitlab_init'
+require_relative 'gitlab_metrics'
+
+class GitlabCustomHook
+ attr_reader :vars, :config
+
+ def initialize(repo_path, key_id)
+ @repo_path = repo_path
+ @vars = { 'GL_ID' => key_id }
+ @config = GitlabConfig.new
+ end
+
+ def pre_receive(changes)
+ GitlabMetrics.measure("pre-receive-hook") do
+ find_hooks('pre-receive').all? do |hook|
+ call_receive_hook(hook, changes)
+ end
+ end
+ end
+
+ def post_receive(changes)
+ GitlabMetrics.measure("post-receive-hook") do
+ find_hooks('post-receive').all? do |hook|
+ call_receive_hook(hook, changes)
+ end
+ end
+ end
+
+ def update(ref_name, old_value, new_value)
+ GitlabMetrics.measure("update-hook") do
+ find_hooks('update').all? do |hook|
+ system(vars, hook, ref_name, old_value, new_value)
+ end
+ end
+ end
+
+ private
+
+ def call_receive_hook(hook, changes)
+ # Prepare the hook subprocess. Attach a pipe to its stdin, and merge
+ # both its stdout and stderr into our own stdout.
+ stdin_reader, stdin_writer = IO.pipe
+ hook_pid = spawn(vars, hook, in: stdin_reader, err: :out)
+ stdin_reader.close
+
+ # Submit changes to the hook via its stdin.
+ begin
+ IO.copy_stream(StringIO.new(changes), stdin_writer)
+ rescue Errno::EPIPE # rubocop:disable Lint/HandleExceptions
+ # It is not an error if the hook does not consume all of its input.
+ end
+
+ # Close the pipe to let the hook know there is no further input.
+ stdin_writer.close
+
+ Process.wait(hook_pid)
+ $?.success?
+ end
+
+ # lookup hook files in this order:
+ #
+ # 1. <repository>.git/custom_hooks/<hook_name> - per project hook
+ # 2. <repository>.git/custom_hooks/<hook_name>.d/* - per project hooks
+ # 3. <repository>.git/hooks/<hook_name>.d/* - global hooks
+ #
+ def find_hooks(hook_name)
+ hook_files = []
+
+ # <repository>.git/custom_hooks/<hook_name>
+ project_custom_hook_file = File.join(@repo_path, 'custom_hooks', hook_name)
+ hook_files.push(project_custom_hook_file) if File.executable?(project_custom_hook_file)
+
+ # <repository>.git/custom_hooks/<hook_name>.d/*
+ project_custom_hooks_dir = File.join(@repo_path, 'custom_hooks', "#{hook_name}.d")
+ hook_files += match_hook_files(project_custom_hooks_dir)
+
+ # <repository>.git/hooks/<hook_name>.d/* OR <custom_hook_dir>/<hook_name>.d/*
+ global_custom_hooks_parent = config.custom_hooks_dir(default: File.join(@repo_path, 'hooks'))
+ global_custom_hooks_dir = File.join(global_custom_hooks_parent, "#{hook_name}.d")
+ hook_files += match_hook_files(global_custom_hooks_dir)
+
+ hook_files
+ end
+
+ # match files from path:
+ # 1. file must be executable
+ # 2. file must not match backup file
+ #
+ # the resulting list is sorted
+ def match_hook_files(path)
+ return [] unless Dir.exist?(path)
+
+ Dir["#{path}/*"].select do |f|
+ !f.end_with?('~') && File.executable?(f)
+ end.sort
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_init.rb b/ruby/vendor/gitlab-shell/lib/gitlab_init.rb
new file mode 100644
index 000000000..0e1458a7b
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_init.rb
@@ -0,0 +1,7 @@
+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 = true
+
+require_relative 'gitlab_config'
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_keys.rb b/ruby/vendor/gitlab-shell/lib/gitlab_keys.rb
new file mode 100644
index 000000000..0600a1877
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_keys.rb
@@ -0,0 +1,190 @@
+require 'timeout'
+
+require_relative 'gitlab_config'
+require_relative 'gitlab_logger'
+require_relative 'gitlab_metrics'
+
+class GitlabKeys # rubocop:disable Metrics/ClassLength
+ class KeyError < StandardError; end
+
+ attr_accessor :auth_file, :key
+
+ 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
+
+ def initialize
+ @command = ARGV.shift
+ @key_id = ARGV.shift
+ key = ARGV.shift
+ @key = key.dup if key
+ @auth_file = GitlabConfig.new.auth_file
+ end
+
+ def exec
+ GitlabMetrics.measure("command-#{@command}") do
+ case @command
+ when 'add-key'
+ add_key
+ when 'batch-add-keys'
+ batch_add_keys
+ when 'rm-key'
+ rm_key
+ when 'list-keys'
+ list_keys
+ when 'list-key-ids'
+ list_key_ids
+ when 'clear'
+ clear
+ when 'check-permissions'
+ check_permissions
+ else
+ $logger.warn('Attempt to execute invalid gitlab-keys command', command: @command.inspect)
+ puts 'not allowed'
+ false
+ end
+ end
+ end
+
+ protected
+
+ def add_key
+ lock do
+ $logger.info('Adding key', key_id: @key_id, public_key: @key)
+ auth_line = self.class.key_line(@key_id, @key)
+ open_auth_file('a') { |file| file.puts(auth_line) }
+ end
+ true
+ end
+
+ def list_keys
+ $logger.info 'Listing all keys'
+ keys = ''
+ File.readlines(auth_file).each do |line|
+ # key_id & public_key
+ # command=".../bin/gitlab-shell key-741" ... ssh-rsa AAAAB3NzaDAxx2E\n
+ # ^^^^^^^ ^^^^^^^^^^^^^^^
+ matches = /^command=\".+?\s+(.+?)\".+?(?:ssh|ecdsa)-.*?\s(.+)\s*.*\n*$/.match(line)
+ keys << "#{matches[1]} #{matches[2]}\n" unless matches.nil?
+ end
+ keys
+ end
+
+ def list_key_ids
+ $logger.info 'Listing all key IDs'
+ open_auth_file('r') do |f|
+ f.each_line do |line|
+ matchd = line.match(/key-(\d+)/)
+ next unless matchd
+ puts matchd[1]
+ end
+ end
+ end
+
+ def batch_add_keys
+ lock(300) do # Allow 300 seconds (5 minutes) for batch_add_keys
+ open_auth_file('a') do |file|
+ stdin.each_line do |input|
+ tokens = input.strip.split("\t")
+ abort("#{$0}: invalid input #{input.inspect}") unless tokens.count == 2
+ key_id, public_key = tokens
+ $logger.info('Adding key', key_id: key_id, public_key: public_key)
+ file.puts(self.class.key_line(key_id, public_key))
+ end
+ end
+ end
+ true
+ end
+
+ def stdin
+ $stdin
+ end
+
+ def rm_key
+ lock do
+ $logger.info('Removing key', key_id: @key_id)
+ open_auth_file('r+') do |f|
+ while line = f.gets # rubocop:disable Lint/AssignmentInCondition
+ next unless line.start_with?("command=\"#{self.class.command_key(@key_id)}\"")
+ f.seek(-line.length, IO::SEEK_CUR)
+ # Overwrite the line with #'s. Because the 'line' variable contains
+ # a terminating '\n', we write line.length - 1 '#' characters.
+ f.write('#' * (line.length - 1))
+ end
+ end
+ end
+ true
+ end
+
+ def clear
+ open_auth_file('w') { |file| file.puts '# Managed by gitlab-shell' }
+ true
+ end
+
+ def check_permissions
+ open_auth_file(File::RDWR | File::CREAT) { true }
+ rescue => ex
+ puts "error: could not open #{auth_file}: #{ex}"
+ if File.exist?(auth_file)
+ system('ls', '-l', auth_file)
+ else
+ # Maybe the parent directory is not writable?
+ system('ls', '-ld', File.dirname(auth_file))
+ end
+ false
+ end
+
+ def lock(timeout = 10)
+ File.open(lock_file, "w+") do |f|
+ begin
+ f.flock File::LOCK_EX
+ Timeout.timeout(timeout) { yield }
+ ensure
+ f.flock File::LOCK_UN
+ end
+ end
+ end
+
+ def lock_file
+ @lock_file ||= auth_file + '.lock'
+ end
+
+ def open_auth_file(mode)
+ open(auth_file, mode, 0o600) do |file|
+ file.chmod(0o600)
+ yield file
+ end
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_lfs_authentication.rb b/ruby/vendor/gitlab-shell/lib/gitlab_lfs_authentication.rb
new file mode 100644
index 000000000..ccd6d69f0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_lfs_authentication.rb
@@ -0,0 +1,30 @@
+require 'base64'
+require 'json'
+
+class GitlabLfsAuthentication
+ attr_accessor :username, :lfs_token, :repository_http_path
+
+ def initialize(username, lfs_token, repository_http_path)
+ @username = username
+ @lfs_token = lfs_token
+ @repository_http_path = repository_http_path
+ end
+
+ def self.build_from_json(json)
+ values = JSON.parse(json)
+ new(values['username'], values['lfs_token'], values['repository_http_path'])
+ rescue
+ nil
+ end
+
+ def authentication_payload
+ authorization = {
+ header: {
+ Authorization: "Basic #{Base64.strict_encode64("#{username}:#{lfs_token}")}"
+ },
+ href: "#{repository_http_path}/info/lfs/"
+ }
+
+ JSON.generate(authorization)
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_logger.rb b/ruby/vendor/gitlab-shell/lib/gitlab_logger.rb
new file mode 100644
index 000000000..67f6030dd
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_logger.rb
@@ -0,0 +1,120 @@
+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)
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_metrics.rb b/ruby/vendor/gitlab-shell/lib/gitlab_metrics.rb
new file mode 100644
index 000000000..917a48918
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_metrics.rb
@@ -0,0 +1,59 @@
+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
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_net.rb b/ruby/vendor/gitlab-shell/lib/gitlab_net.rb
new file mode 100644
index 000000000..a84ba84ee
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_net.rb
@@ -0,0 +1,165 @@
+require 'net/http'
+require 'openssl'
+require 'json'
+
+require_relative 'gitlab_config'
+require_relative 'gitlab_access'
+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
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_net/errors.rb b/ruby/vendor/gitlab-shell/lib/gitlab_net/errors.rb
new file mode 100644
index 000000000..f32b3d826
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_net/errors.rb
@@ -0,0 +1,4 @@
+class GitlabNet
+ class ApiUnreachableError < StandardError; end
+ class NotFound < StandardError; end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_post_receive.rb b/ruby/vendor/gitlab-shell/lib/gitlab_post_receive.rb
new file mode 100644
index 000000000..2d412b91b
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_post_receive.rb
@@ -0,0 +1,119 @@
+require_relative 'gitlab_init'
+require_relative 'gitlab_net'
+require_relative 'gitlab_metrics'
+require 'json'
+require 'base64'
+require 'securerandom'
+
+class GitlabPostReceive
+ attr_reader :config, :gl_repository, :repo_path, :changes, :jid
+
+ def initialize(gl_repository, repo_path, actor, changes, push_options)
+ @config = GitlabConfig.new
+ @gl_repository = gl_repository
+ @repo_path = repo_path.strip
+ @actor = actor
+ @changes = changes
+ @push_options = push_options
+ @jid = SecureRandom.hex(12)
+ end
+
+ def exec
+ response = GitlabMetrics.measure("post-receive") do
+ api.post_receive(gl_repository, @actor, changes, @push_options)
+ end
+
+ return false unless response
+ print_broadcast_message(response['broadcast_message']) if response['broadcast_message']
+ print_merge_request_links(response['merge_request_urls']) if response['merge_request_urls']
+ puts response['redirected_message'] if response['redirected_message']
+ puts response['project_created_message'] if response['project_created_message']
+
+ response['reference_counter_decreased']
+ rescue GitlabNet::ApiUnreachableError
+ false
+ end
+
+ protected
+
+ def api
+ @api ||= GitlabNet.new
+ end
+
+ def print_merge_request_links(merge_request_urls)
+ return if merge_request_urls.empty?
+ puts
+ merge_request_urls.each { |mr| print_merge_request_link(mr) }
+ end
+
+ def print_merge_request_link(merge_request)
+ message =
+ if merge_request["new_merge_request"]
+ "To create a merge request for #{merge_request['branch_name']}, visit:"
+ else
+ "View merge request for #{merge_request['branch_name']}:"
+ end
+
+ puts message
+ puts((" " * 2) + merge_request["url"])
+ puts
+ end
+
+ def print_broadcast_message(message)
+ # A standard terminal window is (at least) 80 characters wide.
+ total_width = 80
+
+ # Git prefixes remote messages with "remote: ", so this width is subtracted
+ # from the width available to us.
+ total_width -= "remote: ".length # rubocop:disable Performance/FixedSize
+
+ # Our centered text shouldn't start or end right at the edge of the window,
+ # so we add some horizontal padding: 2 chars on either side.
+ text_width = total_width - 2 * 2
+
+ # Automatically wrap message at text_width (= 68) characters:
+ # Splits the message up into the longest possible chunks matching
+ # "<between 0 and text_width characters><space or end-of-line>".
+
+ msg_start_idx = 0
+ lines = []
+ while msg_start_idx < message.length
+ parsed_line = parse_broadcast_msg(message[msg_start_idx..-1], text_width)
+ msg_start_idx += parsed_line.length
+ lines.push(parsed_line.strip)
+ end
+
+ puts
+ puts "=" * total_width
+ puts
+
+ lines.each do |line|
+ line.strip!
+
+ # Center the line by calculating the left padding measured in characters.
+ line_padding = [(total_width - line.length) / 2, 0].max
+ puts((" " * line_padding) + line)
+ end
+
+ puts
+ puts "=" * total_width
+ end
+
+ private
+
+ def parse_broadcast_msg(msg, text_length)
+ msg ||= ""
+ # just return msg if shorter than or equal to text length
+ return msg if msg.length <= text_length
+
+ # search for word break shorter than text length
+ truncate_to_space = msg.match(/\A(.{,#{text_length}})(?=\s|$)(\s*)/).to_s
+
+ if truncate_to_space.empty?
+ # search for word break longer than text length
+ truncate_to_space = msg.match(/\A\S+/).to_s
+ end
+
+ truncate_to_space
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/lib/gitlab_shell.rb b/ruby/vendor/gitlab-shell/lib/gitlab_shell.rb
new file mode 100644
index 000000000..c1bb97627
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/gitlab_shell.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+require 'shellwords'
+require 'pathname'
+
+require_relative 'gitlab_net'
+require_relative 'gitlab_metrics'
+require_relative 'action'
+
+class GitlabShell # rubocop:disable Metrics/ClassLength
+ 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, :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']
+ @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)
+ 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
+ $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable"
+ false
+ rescue AccessDeniedError => ex
+ $logger.warn('Access denied', command: origin_cmd, user: log_username)
+ $stderr.puts "GitLab: #{ex.message}"
+ false
+ rescue DisallowedCommandError
+ $logger.warn('Denied disallowed command', command: origin_cmd, user: log_username)
+ $stderr.puts "GitLab: Disallowed command"
+ false
+ rescue InvalidRepositoryPathError
+ $stderr.puts "GitLab: Invalid repository path"
+ false
+ rescue 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_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
diff --git a/ruby/vendor/gitlab-shell/lib/hooks_utils.rb b/ruby/vendor/gitlab-shell/lib/hooks_utils.rb
new file mode 100644
index 000000000..b11317c8d
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/hooks_utils.rb
@@ -0,0 +1,15 @@
+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
diff --git a/ruby/vendor/gitlab-shell/lib/http_helper.rb b/ruby/vendor/gitlab-shell/lib/http_helper.rb
new file mode 100644
index 000000000..c6a4bb845
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/http_helper.rb
@@ -0,0 +1,125 @@
+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
diff --git a/ruby/vendor/gitlab-shell/lib/httpunix.rb b/ruby/vendor/gitlab-shell/lib/httpunix.rb
new file mode 100644
index 000000000..7d00f71d5
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/httpunix.rb
@@ -0,0 +1,54 @@
+# 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
diff --git a/ruby/vendor/gitlab-shell/lib/object_dirs_helper.rb b/ruby/vendor/gitlab-shell/lib/object_dirs_helper.rb
new file mode 100644
index 000000000..e175a0392
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/lib/object_dirs_helper.rb
@@ -0,0 +1,39 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_access_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_access_spec.rb
new file mode 100644
index 000000000..92268e2e7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_access_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+require 'gitlab_access'
+
+describe GitlabAccess do
+ let(:repository_path) { "/home/git/repositories" }
+ let(:repo_name) { 'dzaporozhets/gitlab-ci' }
+ let(:repo_path) { File.join(repository_path, repo_name) + ".git" }
+ let(:api) do
+ double(GitlabNet).tap do |api|
+ allow(api).to receive(:check_access).and_return(GitAccessStatus.new(true,
+ '200',
+ 'ok',
+ gl_repository: 'project-1',
+ gl_id: 'user-123',
+ gl_username: 'testuser',
+ git_config_options: ['receive.MaxInputSize=10000'],
+ gitaly: nil,
+ git_protocol: 'version=2'))
+ end
+ end
+ subject do
+ GitlabAccess.new(nil, repo_path, 'key-123', 'wow', 'ssh').tap do |access|
+ allow(access).to receive(:exec_cmd).and_return(:exec_called)
+ allow(access).to receive(:api).and_return(api)
+ end
+ end
+
+ before do
+ allow_any_instance_of(GitlabConfig).to receive(:repos_path).and_return(repository_path)
+ end
+
+ describe :initialize do
+ it { expect(subject.repo_path).to eq(repo_path) }
+ it { expect(subject.changes).to eq(['wow']) }
+ it { expect(subject.protocol).to eq('ssh') }
+ end
+
+ describe "#exec" do
+ context "access is granted" do
+ it "returns true" do
+ expect(subject.exec).to be_truthy
+ end
+ end
+
+ context "access is denied" do
+ before do
+ allow(api).to receive(:check_access).and_return(GitAccessStatus.new(
+ false,
+ '401',
+ 'denied',
+ gl_repository: nil,
+ gl_id: nil,
+ gl_username: nil,
+ git_config_options: nil,
+ gitaly: nil,
+ git_protocol: nil
+ ))
+ end
+
+ it "returns false" do
+ expect(subject.exec).to be_falsey
+ end
+ end
+
+ context "API connection fails" do
+ before do
+ allow(api).to receive(:check_access).and_raise(GitlabNet::ApiUnreachableError)
+ end
+
+ it "returns false" do
+ expect(subject.exec).to be_falsey
+ end
+ end
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_config_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_config_spec.rb
new file mode 100644
index 000000000..d12b657be
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_config_spec.rb
@@ -0,0 +1,40 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_custom_hook_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_custom_hook_spec.rb
new file mode 100644
index 000000000..540cd2b0a
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_custom_hook_spec.rb
@@ -0,0 +1,295 @@
+# coding: utf-8
+require 'spec_helper'
+require 'gitlab_custom_hook'
+
+describe GitlabCustomHook do
+ let(:original_root_path) { ROOT_PATH }
+ let(:tmp_repo_path) { File.join(original_root_path, 'tmp', 'repo.git') }
+ let(:tmp_root_path) { File.join(original_root_path, 'tmp') }
+ let(:global_custom_hooks_path) { global_hook_path('custom_global_hooks') }
+ let(:hook_ok) { File.join(original_root_path, 'spec', 'support', 'hook_ok') }
+ let(:hook_fail) { File.join(original_root_path, 'spec', 'support', 'hook_fail') }
+ let(:hook_gl_id) { File.join(original_root_path, 'spec', 'support', 'gl_id_test_hook') }
+
+ let(:vars) { { "GL_ID" => "key_1" } }
+ let(:old_value) { "old-value" }
+ let(:new_value) { "new-value" }
+ let(:ref_name) { "name/of/ref" }
+ let(:changes) { "#{old_value} #{new_value} #{ref_name}\n" }
+
+ let(:gitlab_custom_hook) { GitlabCustomHook.new(tmp_repo_path, 'key_1') }
+
+ def hook_path(path)
+ File.join(tmp_repo_path, path.split('/'))
+ end
+
+ def global_hook_path(path)
+ File.join(tmp_root_path, path.split('/'))
+ end
+
+ def create_hook(path, which)
+ FileUtils.ln_sf(which, hook_path(path))
+ end
+
+ # global hooks multiplexed
+ def create_global_hooks_d(which, hook_name = 'hook')
+ create_hook('hooks/pre-receive.d/' + hook_name, which)
+ create_hook('hooks/update.d/' + hook_name, which)
+ create_hook('hooks/post-receive.d/' + hook_name, which)
+ end
+
+ # repo hooks
+ def create_repo_hooks(which)
+ create_hook('custom_hooks/pre-receive', which)
+ create_hook('custom_hooks/update', which)
+ create_hook('custom_hooks/post-receive', which)
+ end
+
+ # repo hooks multiplexed
+ def create_repo_hooks_d(which, hook_name = 'hook')
+ create_hook('custom_hooks/pre-receive.d/' + hook_name, which)
+ create_hook('custom_hooks/update.d/' + hook_name, which)
+ create_hook('custom_hooks/post-receive.d/' + hook_name, which)
+ end
+
+ def cleanup_hook_setup
+ FileUtils.rm_rf(File.join(tmp_repo_path))
+ FileUtils.rm_rf(File.join(global_custom_hooks_path))
+ FileUtils.rm_rf(File.join(tmp_root_path, 'hooks'))
+ FileUtils.rm_f(File.join(tmp_root_path, 'config.yml'))
+ end
+
+ def expect_call_receive_hook(path)
+ expect(gitlab_custom_hook)
+ .to receive(:call_receive_hook)
+ .with(hook_path(path), changes)
+ .and_call_original
+ end
+
+ def expect_call_update_hook(path)
+ expect(gitlab_custom_hook)
+ .to receive(:system)
+ .with(vars, hook_path(path), ref_name, old_value, new_value)
+ .and_call_original
+ end
+
+ # setup paths
+ # <repository>.git/hooks/ - symlink to gitlab-shell/hooks global dir
+ # <repository>.git/hooks/<hook_name> - executed by git itself, this is gitlab-shell/hooks/<hook_name>
+ # <repository>.git/hooks/<hook_name>.d/* - global hooks: all executable files (minus editor backup files)
+ # <repository>.git/custom_hooks/<hook_name> - per project hook (this is already existing behavior)
+ # <repository>.git/custom_hooks/<hook_name>.d/* - per project hooks
+ #
+ # custom hooks are invoked in such way that first failure prevents other scripts being ran
+ # as global scripts are ran first, failing global skips repo hooks
+
+ before do
+ cleanup_hook_setup
+
+ FileUtils.mkdir_p(File.join(tmp_repo_path, 'custom_hooks'))
+ FileUtils.mkdir_p(File.join(tmp_root_path, 'hooks'))
+
+ ['pre-receive', 'update', 'post-receive'].each do |hook|
+ FileUtils.mkdir_p(File.join(tmp_repo_path, 'custom_hooks', "#{hook}.d"))
+ FileUtils.mkdir_p(File.join(tmp_root_path, 'hooks', "#{hook}.d"))
+ end
+
+ FileUtils.symlink(File.join(tmp_root_path, 'hooks'), File.join(tmp_repo_path, 'hooks'))
+ FileUtils.symlink(File.join(ROOT_PATH, 'config.yml.example'), File.join(tmp_root_path, 'config.yml'))
+
+ stub_const('ROOT_PATH', tmp_root_path)
+ end
+
+ after do
+ cleanup_hook_setup
+ end
+
+ context 'with gl_id_test_hook as repo hook' do
+ before do
+ create_repo_hooks(hook_gl_id)
+ end
+
+ context 'pre_receive hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ end
+ end
+
+ context 'post_receive hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(true)
+ end
+ end
+
+ context 'update hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(true)
+ end
+ end
+ end
+
+ context 'with gl_id_test_hook as global hook' do
+ before do
+ create_global_hooks_d(hook_gl_id)
+ end
+
+ context 'pre_receive hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ end
+ end
+
+ context 'post_receive hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(true)
+ end
+ end
+
+ context 'update hook' do
+ it 'passes GL_ID variable to hook' do
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(true)
+ end
+ end
+ end
+
+ context "having no hooks" do
+ it "returns true" do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(true)
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(true)
+ end
+ end
+
+ context "having only successful repo hooks" do
+ before do
+ create_repo_hooks(hook_ok)
+ end
+
+ it "returns true" do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(true)
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(true)
+ end
+ end
+
+ context "having both successful repo and global hooks" do
+ before do
+ create_repo_hooks(hook_ok)
+ create_global_hooks_d(hook_ok)
+ end
+
+ it "returns true" do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(true)
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(true)
+ end
+ end
+
+ context "having failing repo and successful global hooks" do
+ before do
+ create_repo_hooks_d(hook_fail)
+ create_global_hooks_d(hook_ok)
+ end
+
+ it "returns false" do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(false)
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(false)
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(false)
+ end
+
+ it "only executes the global hook" do
+ expect_call_receive_hook("custom_hooks/pre-receive.d/hook")
+ expect_call_update_hook("custom_hooks/update.d/hook")
+ expect_call_receive_hook("custom_hooks/post-receive.d/hook")
+
+ gitlab_custom_hook.pre_receive(changes)
+ gitlab_custom_hook.update(ref_name, old_value, new_value)
+ gitlab_custom_hook.post_receive(changes)
+ end
+ end
+
+ context "having successful repo but failing global hooks" do
+ before do
+ create_repo_hooks_d(hook_ok)
+ create_global_hooks_d(hook_fail)
+ end
+
+ it "returns false" do
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(false)
+ expect(gitlab_custom_hook.update(ref_name, old_value, new_value)).to eq(false)
+ expect(gitlab_custom_hook.post_receive(changes)).to eq(false)
+ end
+
+ it "executes the relevant hooks" do
+ expect_call_receive_hook("hooks/pre-receive.d/hook")
+ expect_call_receive_hook("custom_hooks/pre-receive.d/hook")
+ expect_call_update_hook("hooks/update.d/hook")
+ expect_call_update_hook("custom_hooks/update.d/hook")
+ expect_call_receive_hook("hooks/post-receive.d/hook")
+ expect_call_receive_hook("custom_hooks/post-receive.d/hook")
+
+ gitlab_custom_hook.pre_receive(changes)
+ gitlab_custom_hook.update(ref_name, old_value, new_value)
+ gitlab_custom_hook.post_receive(changes)
+ end
+ end
+
+ context "executing hooks in expected order" do
+ before do
+ create_repo_hooks_d(hook_ok, '01-test')
+ create_repo_hooks_d(hook_ok, '02-test')
+ create_global_hooks_d(hook_ok, '03-test')
+ create_global_hooks_d(hook_ok, '04-test')
+ end
+
+ it "executes hooks in order" do
+ expect_call_receive_hook("custom_hooks/pre-receive.d/01-test").ordered
+ expect_call_receive_hook("custom_hooks/pre-receive.d/02-test").ordered
+ expect_call_receive_hook("hooks/pre-receive.d/03-test").ordered
+ expect_call_receive_hook("hooks/pre-receive.d/04-test").ordered
+
+ expect_call_update_hook("custom_hooks/update.d/01-test").ordered
+ expect_call_update_hook("custom_hooks/update.d/02-test").ordered
+ expect_call_update_hook("hooks/update.d/03-test").ordered
+ expect_call_update_hook("hooks/update.d/04-test").ordered
+
+ expect_call_receive_hook("custom_hooks/post-receive.d/01-test").ordered
+ expect_call_receive_hook("custom_hooks/post-receive.d/02-test").ordered
+ expect_call_receive_hook("hooks/post-receive.d/03-test").ordered
+ expect_call_receive_hook("hooks/post-receive.d/04-test").ordered
+
+ gitlab_custom_hook.pre_receive(changes)
+ gitlab_custom_hook.update(ref_name, old_value, new_value)
+ gitlab_custom_hook.post_receive(changes)
+ end
+ end
+
+ context "when the custom_hooks_dir config option is set" do
+ before do
+ allow(gitlab_custom_hook.config).to receive(:custom_hooks_dir).and_return(global_custom_hooks_path)
+
+ FileUtils.mkdir_p(File.join(global_custom_hooks_path, "pre-receive.d"))
+ FileUtils.ln_sf(hook_ok, File.join(global_custom_hooks_path, "pre-receive.d", "hook"))
+
+ create_global_hooks_d(hook_fail)
+ end
+
+ it "finds hooks in that directory" do
+ expect(gitlab_custom_hook)
+ .to receive(:call_receive_hook)
+ .with(global_hook_path("custom_global_hooks/pre-receive.d/hook"), changes)
+ .and_call_original
+
+ expect(gitlab_custom_hook.pre_receive(changes)).to eq(true)
+ end
+
+ it "does not execute hooks in the default location" do
+ expect(gitlab_custom_hook)
+ .not_to receive(:call_receive_hook)
+ .with("hooks/pre-receive.d/hook", changes)
+ .and_call_original
+
+ gitlab_custom_hook.pre_receive(changes)
+ end
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_keys_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_keys_spec.rb
new file mode 100644
index 000000000..f86d00d06
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_keys_spec.rb
@@ -0,0 +1,372 @@
+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
+
+ describe :initialize do
+ let(:gitlab_keys) { build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') }
+
+ it { expect(gitlab_keys.key).to eq('ssh-rsa AAAAB3NzaDAxx2E') }
+ it { expect(gitlab_keys.instance_variable_get(:@command)).to eq('add-key') }
+ it { expect(gitlab_keys.instance_variable_get(:@key_id)).to eq('key-741') }
+ end
+
+ describe :add_key do
+ let(:gitlab_keys) { build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') }
+
+ it "adds a line at the end of the file" do
+ create_authorized_keys_fixture
+ gitlab_keys.send :add_key
+ auth_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-741\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaDAxx2E"
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line}\n")
+ end
+
+ context "without file writing" do
+ before { allow(gitlab_keys).to receive(:open) }
+ before { create_authorized_keys_fixture }
+
+ it "should log an add-key event" do
+ expect($logger).to receive(:info).with("Adding key", {:key_id=>"key-741", :public_key=>"ssh-rsa AAAAB3NzaDAxx2E"})
+ gitlab_keys.send :add_key
+ end
+
+ it "should return true" do
+ expect(gitlab_keys.send(:add_key)).to be_truthy
+ end
+ end
+ end
+
+ describe :list_keys do
+ let(:gitlab_keys) do
+ build_gitlab_keys('add-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E')
+ end
+
+ it 'adds a key and lists it' do
+ create_authorized_keys_fixture
+ gitlab_keys.send :add_key
+ auth_line1 = 'key-741 AAAAB3NzaDAxx2E'
+ expect(gitlab_keys.send(:list_keys)).to eq("#{auth_line1}\n")
+ end
+ end
+
+ describe :list_key_ids do
+ let(:gitlab_keys) { build_gitlab_keys('list-key-ids') }
+ before do
+ create_authorized_keys_fixture(
+ existing_content:
+ "key-1\tssh-dsa AAA\nkey-2\tssh-rsa BBB\nkey-3\tssh-rsa CCC\nkey-9000\tssh-rsa DDD\n"
+ )
+ end
+
+ it 'outputs the key IDs, separated by newlines' do
+ expect { gitlab_keys.send(:list_key_ids) }.to output("1\n2\n3\n9000\n").to_stdout
+ end
+ end
+
+ describe :batch_add_keys do
+ let(:gitlab_keys) { build_gitlab_keys('batch-add-keys') }
+ let(:fake_stdin) { StringIO.new("key-12\tssh-dsa ASDFASGADG\nkey-123\tssh-rsa GFDGDFSGSDFG\n", 'r') }
+ before do
+ create_authorized_keys_fixture
+ allow(gitlab_keys).to receive(:stdin).and_return(fake_stdin)
+ end
+
+ it "adds lines at the end of the file" do
+ gitlab_keys.send :batch_add_keys
+ auth_line1 = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-12\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dsa ASDFASGADG"
+ auth_line2 = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-123\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa GFDGDFSGSDFG"
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line1}\n#{auth_line2}\n")
+ end
+
+ context "with invalid input" do
+ let(:fake_stdin) { StringIO.new("key-12\tssh-dsa ASDFASGADG\nkey-123\tssh-rsa GFDGDFSGSDFG\nfoo\tbar\tbaz\n", 'r') }
+
+ it "aborts" do
+ expect(gitlab_keys).to receive(:abort)
+ gitlab_keys.send :batch_add_keys
+ end
+ end
+
+ context "without file writing" do
+ before do
+ expect(gitlab_keys).to receive(:open).and_yield(double(:file, puts: nil, chmod: nil))
+ end
+
+ it "should log an add-key event" do
+ expect($logger).to receive(:info).with("Adding key", key_id: 'key-12', public_key: "ssh-dsa ASDFASGADG")
+ expect($logger).to receive(:info).with("Adding key", key_id: 'key-123', public_key: "ssh-rsa GFDGDFSGSDFG")
+ gitlab_keys.send :batch_add_keys
+ end
+
+ it "should return true" do
+ expect(gitlab_keys.send(:batch_add_keys)).to be_truthy
+ end
+ end
+ end
+
+ describe :stdin do
+ let(:gitlab_keys) { build_gitlab_keys }
+ subject { gitlab_keys.send :stdin }
+ before { $stdin = 1 }
+
+ it { is_expected.to equal(1) }
+ end
+
+ describe :rm_key do
+ let(:gitlab_keys) { build_gitlab_keys('rm-key', 'key-741', 'ssh-rsa AAAAB3NzaDAxx2E') }
+
+ it "removes the right line" do
+ create_authorized_keys_fixture
+ other_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E"
+ delete_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E"
+ open(tmp_authorized_keys_path, 'a') do |auth_file|
+ auth_file.puts delete_line
+ auth_file.puts other_line
+ end
+ gitlab_keys.send :rm_key
+ erased_line = delete_line.gsub(/./, '#')
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{erased_line}\n#{other_line}\n")
+ end
+
+ context "without file writing" do
+ before do
+ allow(gitlab_keys).to receive(:open)
+ allow(gitlab_keys).to receive(:lock).and_yield
+ end
+
+ it "should log an rm-key event" do
+ expect($logger).to receive(:info).with("Removing key", key_id: "key-741")
+ gitlab_keys.send :rm_key
+ end
+
+ it "should return true" do
+ expect(gitlab_keys.send(:rm_key)).to be_truthy
+ end
+ end
+
+ context 'without key content' do
+ let(:gitlab_keys) { build_gitlab_keys('rm-key', 'key-741') }
+
+ it "removes the right line by key ID" do
+ create_authorized_keys_fixture
+ other_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E"
+ delete_line = "command=\"#{ROOT_PATH}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E"
+ open(tmp_authorized_keys_path, 'a') do |auth_file|
+ auth_file.puts delete_line
+ auth_file.puts other_line
+ end
+ gitlab_keys.send :rm_key
+ erased_line = delete_line.gsub(/./, '#')
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{erased_line}\n#{other_line}\n")
+ end
+ end
+ end
+
+ describe :clear do
+ let(:gitlab_keys) { build_gitlab_keys('clear') }
+
+ it "should return true" do
+ allow(gitlab_keys).to receive(:open)
+ expect(gitlab_keys.send(:clear)).to be_truthy
+ end
+ end
+
+ describe :check_permissions do
+ let(:gitlab_keys) { build_gitlab_keys('check-permissions') }
+
+ it 'returns true when the file can be opened' do
+ create_authorized_keys_fixture
+ expect(gitlab_keys.exec).to eq(true)
+ end
+
+ it 'returns false if opening raises an exception' do
+ expect(gitlab_keys).to receive(:open_auth_file).and_raise("imaginary error")
+ expect(gitlab_keys.exec).to eq(false)
+ end
+
+ it 'creates the keys file if it does not exist' do
+ create_authorized_keys_fixture
+ FileUtils.rm(tmp_authorized_keys_path)
+ expect(gitlab_keys.exec).to eq(true)
+ expect(File.exist?(tmp_authorized_keys_path)).to eq(true)
+ end
+ end
+
+ describe :exec do
+ it 'add-key arg should execute add_key method' do
+ gitlab_keys = build_gitlab_keys('add-key')
+ expect(gitlab_keys).to receive(:add_key)
+ gitlab_keys.exec
+ end
+
+ it 'batch-add-keys arg should execute batch_add_keys method' do
+ gitlab_keys = build_gitlab_keys('batch-add-keys')
+ expect(gitlab_keys).to receive(:batch_add_keys)
+ gitlab_keys.exec
+ end
+
+ it 'rm-key arg should execute rm_key method' do
+ gitlab_keys = build_gitlab_keys('rm-key')
+ expect(gitlab_keys).to receive(:rm_key)
+ gitlab_keys.exec
+ end
+
+ it 'clear arg should execute clear method' do
+ gitlab_keys = build_gitlab_keys('clear')
+ expect(gitlab_keys).to receive(:clear)
+ gitlab_keys.exec
+ end
+
+ it 'check-permissions arg should execute check_permissions method' do
+ gitlab_keys = build_gitlab_keys('check-permissions')
+ expect(gitlab_keys).to receive(:check_permissions)
+ gitlab_keys.exec
+ end
+
+ it 'should puts message if unknown command arg' do
+ gitlab_keys = build_gitlab_keys('change-key')
+ expect(gitlab_keys).to receive(:puts).with('not allowed')
+ gitlab_keys.exec
+ end
+
+ it 'should log a warning on unknown commands' do
+ gitlab_keys = build_gitlab_keys('nooope')
+ allow(gitlab_keys).to receive(:puts).and_return(nil)
+ expect($logger).to receive(:warn).with("Attempt to execute invalid gitlab-keys command", command: '"nooope"')
+ gitlab_keys.exec
+ end
+ end
+
+ describe :lock do
+ before do
+ allow_any_instance_of(GitlabKeys).to receive(:lock_file).and_return(tmp_lock_file_path)
+ end
+
+ it "should raise exception if operation lasts more then timeout" do
+ key = GitlabKeys.new
+ expect do
+ key.send :lock, 1 do
+ sleep 2
+ end
+ end.to raise_error(Timeout::Error, 'execution expired')
+ end
+
+ it "should actually lock file" do
+ $global = ""
+ key = GitlabKeys.new
+
+ thr1 = Thread.new do
+ key.send :lock do
+ # Put bigger sleep here to test if main thread will
+ # wait for lock file released before executing code
+ sleep 1
+ $global << "foo"
+ end
+ end
+
+ # make sure main thread start lock command after
+ # thread above
+ sleep 0.5
+
+ key.send :lock do
+ $global << "bar"
+ end
+
+ thr1.join
+ expect($global).to eq("foobar")
+ end
+ end
+
+ def build_gitlab_keys(*args)
+ argv(*args)
+ GitlabKeys.new
+ end
+
+ def argv(*args)
+ args.each_with_index do |arg, i|
+ ARGV[i] = arg.freeze
+ end
+ end
+
+ def create_authorized_keys_fixture(existing_content: 'existing content')
+ FileUtils.mkdir_p(File.dirname(tmp_authorized_keys_path))
+ open(tmp_authorized_keys_path, 'w') { |file| file.puts(existing_content) }
+ allow(gitlab_keys).to receive(:auth_file).and_return(tmp_authorized_keys_path)
+ end
+
+ def tmp_authorized_keys_path
+ File.join(ROOT_PATH, 'tmp', 'authorized_keys')
+ end
+
+ def tmp_lock_file_path
+ tmp_authorized_keys_path + '.lock'
+ end
+
+ def capture_stdout(&blk)
+ old = $stdout
+ $stdout = fake = StringIO.new
+ blk.call
+ fake.string
+ ensure
+ $stdout = old
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_lfs_authentication_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_lfs_authentication_spec.rb
new file mode 100644
index 000000000..81b53a7bf
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_lfs_authentication_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+require 'gitlab_lfs_authentication'
+require 'json'
+
+describe GitlabLfsAuthentication do
+ subject do
+ GitlabLfsAuthentication.build_from_json(
+ JSON.generate(
+ {
+ username: 'dzaporozhets',
+ lfs_token: 'wsnys8Zm8Jn7zyhHTAAK',
+ repository_http_path: 'http://gitlab.dev/repo'
+ }
+ )
+ )
+ 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
+ result = "{\"header\":{\"Authorization\":\"Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL\"},\"href\":\"http://gitlab.dev/repo/info/lfs/\"}"
+
+ it { expect(subject.authentication_payload).to eq(result) }
+
+ it 'should be a 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
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_logger_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_logger_spec.rb
new file mode 100644
index 000000000..a9cd3fb0c
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_logger_spec.rb
@@ -0,0 +1,145 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_metrics_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_metrics_spec.rb
new file mode 100644
index 000000000..00e94b590
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_metrics_spec.rb
@@ -0,0 +1,40 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_net_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_net_spec.rb
new file mode 100644
index 000000000..c6245d4e3
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_net_spec.rb
@@ -0,0 +1,554 @@
+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
+ 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
+ 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
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_post_receive_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_post_receive_spec.rb
new file mode 100644
index 000000000..3cae6319e
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_post_receive_spec.rb
@@ -0,0 +1,270 @@
+# coding: utf-8
+require 'spec_helper'
+require 'gitlab_post_receive'
+
+describe GitlabPostReceive do
+ let(:repository_path) { "/home/git/repositories" }
+ let(:repo_name) { 'dzaporozhets/gitlab-ci' }
+ let(:actor) { 'key-123' }
+ let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
+ let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
+ let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
+ let(:repo_path) { File.join(repository_path, repo_name) + ".git" }
+ let(:gl_repository) { "project-1" }
+ let(:push_options) { [] }
+ let(:gitlab_post_receive) { GitlabPostReceive.new(gl_repository, repo_path, actor, wrongly_encoded_changes, push_options) }
+ let(:broadcast_message) { "test " * 10 + "message " * 10 }
+ let(:enqueued_at) { Time.new(2016, 6, 23, 6, 59) }
+ let(:new_merge_request_urls) do
+ [{
+ 'branch_name' => 'new_branch',
+ 'url' => 'http://localhost/dzaporozhets/gitlab-ci/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch',
+ 'new_merge_request' => true
+ }]
+ end
+ let(:existing_merge_request_urls) do
+ [{
+ 'branch_name' => 'feature_branch',
+ 'url' => 'http://localhost/dzaporozhets/gitlab-ci/merge_requests/1',
+ 'new_merge_request' => false
+ }]
+ end
+
+ before do
+ $logger = double('logger').as_null_object # Global vars are bad
+ allow_any_instance_of(GitlabConfig).to receive(:repos_path).and_return(repository_path)
+ end
+
+ describe "#exec" do
+ let(:response) { { 'reference_counter_decreased' => true } }
+
+ it 'calls the api to notify the execution of the hook' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+
+ context 'merge request urls and broadcast messages' do
+ let(:response) do
+ {
+ 'reference_counter_decreased' => true,
+ 'merge_request_urls' => new_merge_request_urls,
+ 'broadcast_message' => broadcast_message
+ }
+ end
+
+ it 'prints the merge request urls and broadcast message' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+ assert_broadcast_message_printed(gitlab_post_receive)
+ assert_new_mr_printed(gitlab_post_receive)
+
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+
+ context 'when contains long url string at end' do
+ let(:broadcast_message) { "test " * 10 + "message " * 10 + "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url" }
+
+ it 'doesnt truncate url' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+ assert_broadcast_message_printed_keep_long_url_end(gitlab_post_receive)
+ assert_new_mr_printed(gitlab_post_receive)
+
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+ end
+
+ context 'when contains long url string at start' do
+ let(:broadcast_message) { "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url " + "test " * 10 + "message " * 11}
+
+ it 'doesnt truncate url' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+ assert_broadcast_message_printed_keep_long_url_start(gitlab_post_receive)
+ assert_new_mr_printed(gitlab_post_receive)
+
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+ end
+
+ context 'when contains long url string in middle' do
+ let(:broadcast_message) { "test " * 11 + "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url " + "message " * 11}
+
+ it 'doesnt truncate url' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+ assert_broadcast_message_printed_keep_long_url_middle(gitlab_post_receive)
+ assert_new_mr_printed(gitlab_post_receive)
+
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+ end
+
+ end
+
+ context 'when redirected message available' do
+ let(:message) { "This is a redirected message" }
+ let(:response) do
+ {
+ 'reference_counter_decreased' => true,
+ 'redirected_message' => message
+ }
+ end
+
+ it 'prints redirected message' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+ assert_redirected_message_printed(gitlab_post_receive)
+ expect(gitlab_post_receive.exec).to eq(true)
+ end
+
+ context 'when project created message is available' do
+ let(:message) { "This is a created project message" }
+ let(:response) do
+ {
+ 'reference_counter_decreased' => true,
+ 'project_created_message' => message
+ }
+ end
+
+ it 'prints project created message' do
+ expect_any_instance_of(GitlabNet).to receive(:post_receive).and_return(response)
+
+ assert_project_created_message_printed(gitlab_post_receive)
+
+ expect(gitlab_post_receive.exec).to be true
+ end
+ end
+ end
+ end
+
+ private
+
+ def assert_new_mr_printed(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "To create a merge request for new_branch, visit:"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " http://localhost/dzaporozhets/gitlab-ci/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ end
+
+ def assert_existing_mr_printed(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "View merge request for feature_branch:"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " http://localhost/dzaporozhets/gitlab-ci/merge_requests/1"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ end
+
+ def assert_broadcast_message_printed(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " test test test test test test test test test test message message"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message message message message message message message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ end
+
+ def assert_redirected_message_printed(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).with("This is a redirected message")
+ end
+
+ def assert_project_created_message_printed(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).with("This is a created project message")
+ end
+
+ def assert_broadcast_message_printed_keep_long_url_end(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " test test test test test test test test test test message message"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message message message message message message message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ end
+
+ def assert_broadcast_message_printed_keep_long_url_start(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " test test test test test test test test test test message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message message message message message message message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ end
+
+ def assert_broadcast_message_printed_keep_long_url_middle(gitlab_post_receive)
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ expect(gitlab_post_receive).to receive(:puts).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " test test test test test test test test test test test"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "https://localhost:5000/test/a/really/long/url/that/is/in/the/broadcast/message/do-not-truncate-when-url"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message message message message message message message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).with(
+ " message message message"
+ ).ordered
+
+ expect(gitlab_post_receive).to receive(:puts).ordered
+ expect(gitlab_post_receive).to receive(:puts).with(
+ "========================================================================"
+ ).ordered
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_shell_authorized_keys_check_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_shell_authorized_keys_check_spec.rb
new file mode 100644
index 000000000..baaa56049
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_shell_authorized_keys_check_spec.rb
@@ -0,0 +1,117 @@
+require_relative 'spec_helper'
+
+describe 'bin/gitlab-shell-authorized-keys-check' do
+ def original_root_path
+ ROOT_PATH
+ end
+
+ # All this test boilerplate is mostly copy/pasted between
+ # gitlab_shell_gitlab_shell_spec.rb and
+ # gitlab_shell_authorized_keys_check_spec.rb
+ def tmp_root_path
+ @tmp_root_path ||= File.realpath(Dir.mktmpdir)
+ end
+
+ def config_path
+ File.join(tmp_root_path, 'config.yml')
+ end
+
+ def tmp_socket_path
+ # This has to be a relative path shorter than 100 bytes due to
+ # limitations in how Unix sockets work.
+ 'tmp/gitlab-shell-authorized-keys-check-socket'
+ end
+
+ before(:all) do
+ FileUtils.mkdir_p(File.dirname(tmp_socket_path))
+ FileUtils.touch(File.join(tmp_root_path, '.gitlab_shell_secret'))
+
+ @server = HTTPUNIXServer.new(BindAddress: tmp_socket_path)
+ @server.mount_proc('/api/v4/internal/authorized_keys') do |req, res|
+ if req.query['key'] == 'known-rsa-key'
+ res.status = 200
+ res.content_type = 'application/json'
+ res.body = '{"key":"known-rsa-key", "id": 1}'
+ else
+ res.status = 404
+ end
+ end
+
+ @webrick_thread = Thread.new { @server.start }
+
+ sleep(0.1) while @webrick_thread.alive? && @server.status != :Running
+ raise "Couldn't start stub GitlabNet server" unless @server.status == :Running
+
+ File.open(config_path, 'w') do |f|
+ f.write("---\ngitlab_url: http+unix://#{CGI.escape(tmp_socket_path)}\n")
+ end
+
+ copy_dirs = ['bin', 'lib']
+ FileUtils.rm_rf(copy_dirs.map { |d| File.join(tmp_root_path, d) })
+ FileUtils.cp_r(copy_dirs, tmp_root_path)
+ end
+
+ after(:all) do
+ @server.shutdown if @server
+ @webrick_thread.join if @webrick_thread
+ FileUtils.rm_rf(tmp_root_path)
+ end
+
+ let(:gitlab_shell_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell') }
+ let(:authorized_keys_check_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell-authorized-keys-check') }
+
+ it 'succeeds when a valid key is given' do
+ output, status = run!
+
+ expect(output).to eq("command=\"#{gitlab_shell_path} key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty known-rsa-key\n")
+ expect(status).to be_success
+ end
+
+ it 'returns nothing when an unknown key is given' do
+ output, status = run!(key: 'unknown-key')
+
+ expect(output).to eq("# No key was found for unknown-key\n")
+ expect(status).to be_success
+ end
+
+ it' fails when not enough arguments are given' do
+ output, status = run!(key: nil)
+
+ expect(output).to eq('')
+ expect(status).not_to be_success
+ end
+
+ it' fails when too many arguments are given' do
+ output, status = run!(key: ['a', 'b'])
+
+ expect(output).to eq('')
+ expect(status).not_to be_success
+ end
+
+ it 'skips when run as the wrong user' do
+ output, status = run!(expected_user: 'unknown-user')
+
+ expect(output).to eq('')
+ expect(status).to be_success
+ end
+
+ it 'skips when the wrong users connects' do
+ output, status = run!(actual_user: 'unknown-user')
+
+ expect(output).to eq('')
+ expect(status).to be_success
+ end
+
+ def run!(expected_user: 'git', actual_user: 'git', key: 'known-rsa-key')
+ cmd = [
+ authorized_keys_check_path,
+ expected_user,
+ actual_user,
+ key
+ ].flatten.compact
+
+ output = IO.popen(cmd, &:read)
+
+ [output, $?]
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/gitlab_shell_spec.rb b/ruby/vendor/gitlab-shell/spec/gitlab_shell_spec.rb
new file mode 100644
index 000000000..eef572e13
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/gitlab_shell_spec.rb
@@ -0,0 +1,477 @@
+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_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
+ )
+ 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_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_id) { 'user-1' }
+ let(:gl_username) { 'testuser' }
+ let(:git_config_options) { ['receive.MaxInputSize=10000'] }
+ let(:git_protocol) { 'version=2' }
+
+ 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_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
+ 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_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
diff --git a/ruby/vendor/gitlab-shell/spec/hooks_utils_spec.rb b/ruby/vendor/gitlab-shell/spec/hooks_utils_spec.rb
new file mode 100644
index 000000000..2113239a4
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/hooks_utils_spec.rb
@@ -0,0 +1,28 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/httpunix_spec.rb b/ruby/vendor/gitlab-shell/spec/httpunix_spec.rb
new file mode 100644
index 000000000..719a85594
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/httpunix_spec.rb
@@ -0,0 +1,58 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/object_dirs_helper_spec.rb b/ruby/vendor/gitlab-shell/spec/object_dirs_helper_spec.rb
new file mode 100644
index 000000000..c2d0db7f7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/object_dirs_helper_spec.rb
@@ -0,0 +1,95 @@
+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
diff --git a/ruby/vendor/gitlab-shell/spec/spec_helper.rb b/ruby/vendor/gitlab-shell/spec/spec_helper.rb
new file mode 100644
index 000000000..dfdf44984
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/spec_helper.rb
@@ -0,0 +1,16 @@
+require 'rspec-parameterized'
+require 'simplecov'
+SimpleCov.start
+
+require 'gitlab_init'
+
+Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
+
+RSpec.configure do |config|
+ config.run_all_when_everything_filtered = true
+ config.filter_run :focus
+
+ config.before(:each) do
+ stub_const('ROOT_PATH', File.expand_path('..', __dir__))
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/support/gl_id_test_hook b/ruby/vendor/gitlab-shell/spec/support/gl_id_test_hook
new file mode 100755
index 000000000..bf5874da9
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/gl_id_test_hook
@@ -0,0 +1,2 @@
+#!/bin/sh
+printenv GL_ID | grep -q '^key_1$'
diff --git a/ruby/vendor/gitlab-shell/spec/support/hook_fail b/ruby/vendor/gitlab-shell/spec/support/hook_fail
new file mode 100755
index 000000000..4420796f8
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/hook_fail
@@ -0,0 +1,3 @@
+#!/bin/bash
+#echo "fail: $0"
+exit 1
diff --git a/ruby/vendor/gitlab-shell/spec/support/hook_ok b/ruby/vendor/gitlab-shell/spec/support/hook_ok
new file mode 100755
index 000000000..eb1e3bc7f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/hook_ok
@@ -0,0 +1,3 @@
+#!/bin/bash
+#echo "ok: $0"
+exit 0
diff --git a/ruby/vendor/gitlab-shell/spec/support/http_unix_server.rb b/ruby/vendor/gitlab-shell/spec/support/http_unix_server.rb
new file mode 100644
index 000000000..113df57bc
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/http_unix_server.rb
@@ -0,0 +1,35 @@
+require 'webrick'
+
+# like WEBrick::HTTPServer, but listens on UNIX socket
+class HTTPUNIXServer < WEBrick::HTTPServer
+ def initialize(config = {})
+ null_log = WEBrick::Log.new(IO::NULL, 7)
+
+ super(config.merge(Logger: null_log, AccessLog: null_log))
+ end
+
+ def listen(address, port)
+ socket = Socket.unix_server_socket(address)
+ socket.autoclose = false
+ server = UNIXServer.for_fd(socket.fileno)
+ socket.close
+ @listeners << server
+ end
+
+ # Workaround:
+ # https://bugs.ruby-lang.org/issues/10956
+ # Affecting Ruby 2.2
+ # Fix for 2.2 is at https://github.com/ruby/ruby/commit/ab0a64e1
+ # However, this doesn't work with 2.1. The following should work for both:
+ def start(&block)
+ @shutdown_pipe = IO.pipe
+ shutdown_pipe = @shutdown_pipe
+ super(&block)
+ end
+
+ def cleanup_shutdown_pipe(shutdown_pipe)
+ @shutdown_pipe = nil
+ return if !shutdown_pipe
+ super(shutdown_pipe)
+ end
+end
diff --git a/ruby/vendor/gitlab-shell/spec/support/vcr.rb b/ruby/vendor/gitlab-shell/spec/support/vcr.rb
new file mode 100644
index 000000000..a47cb9702
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/vcr.rb
@@ -0,0 +1,7 @@
+require 'vcr'
+
+VCR.configure do |c|
+ c.cassette_library_dir = 'spec/vcr_cassettes'
+ c.hook_into :webmock
+ c.configure_rspec_metadata!
+end
diff --git a/ruby/vendor/gitlab-shell/spec/support/webmock.rb b/ruby/vendor/gitlab-shell/spec/support/webmock.rb
new file mode 100644
index 000000000..ed4fe6d6a
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/support/webmock.rb
@@ -0,0 +1,3 @@
+require 'webmock/rspec'
+
+WebMock.disable_net_connect!(allow_localhost: true)
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-pull.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-pull.yml
new file mode 100644
index 000000000..b07347655
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-pull.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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","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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-html.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-html.yml
new file mode 100644
index 000000000..4adb08802
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-html.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-plain.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-plain.yml
new file mode 100644
index 000000000..a84b7d2d7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404-text-plain.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404.yml
new file mode 100644
index 000000000..e531fcbbe
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-404.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-html.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-html.yml
new file mode 100644
index 000000000..b2738fea7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-html.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-plain.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-plain.yml
new file mode 100644
index 000000000..34532cee2
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found-text-plain.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found.yml
new file mode 100644
index 000000000..f25fa7eaa
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push-project-not-found.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push.yml
new file mode 100644
index 000000000..1636fd507
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/allowed-push.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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","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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-none.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-none.yml
new file mode 100644
index 000000000..8162343c2
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-none.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-ok.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-ok.yml
new file mode 100644
index 000000000..a309c7ed3
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/broadcast_message-ok.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/check-ok.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/check-ok.yml
new file mode 100644
index 000000000..f8ba853ee
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/check-ok.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-json.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-json.yml
new file mode 100644
index 000000000..e50719ddf
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-json.yml
@@ -0,0 +1,40 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-not-json.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-not-json.yml
new file mode 100644
index 000000000..b60a93a6c
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-not-ok-not-json.yml
@@ -0,0 +1,40 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-not-json.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-not-json.yml
new file mode 100644
index 000000000..3bfb39057
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-not-json.yml
@@ -0,0 +1,51 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-with-message.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-with-message.yml
new file mode 100644
index 000000000..9d44a37d2
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok-with-message.yml
@@ -0,0 +1,99 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok.yml
new file mode 100644
index 000000000..9d44a37d2
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/custom-action-ok.yml
@@ -0,0 +1,99 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/discover-ok.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/discover-ok.yml
new file mode 100644
index 000000000..0f17363eb
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/discover-ok.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-pull-disabled.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-pull-disabled.yml
new file mode 100644
index 000000000..23ef3e53c
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-pull-disabled.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-push-disabled.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-push-disabled.yml
new file mode 100644
index 000000000..676e5b8b4
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/http-push-disabled.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-download.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-download.yml
new file mode 100644
index 000000000..e832c9067
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-download.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-upload.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-upload.yml
new file mode 100644
index 000000000..e4fa33569
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/lfs-authenticate-ok-upload.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/notify-post-receive.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/notify-post-receive.yml
new file mode 100644
index 000000000..255f41516
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/notify-post-receive.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive-not-found.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive-not-found.yml
new file mode 100644
index 000000000..e89095d19
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive-not-found.yml
@@ -0,0 +1,42 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive.yml
new file mode 100644
index 000000000..c29f86539
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/post-receive.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive-not-found.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive-not-found.yml
new file mode 100644
index 000000000..5eed672c7
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive-not-found.yml
@@ -0,0 +1,42 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive.yml
new file mode 100644
index 000000000..6072d0ca0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/pre-receive.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-found.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-found.yml
new file mode 100644
index 000000000..fdff8fcbe
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-found.yml
@@ -0,0 +1,44 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-implemented.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-implemented.yml
new file mode 100644
index 000000000..c883dfaf4
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-not-implemented.yml
@@ -0,0 +1,44 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-ok.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-ok.yml
new file mode 100644
index 000000000..358f78800
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-key-ok.yml
@@ -0,0 +1,47 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-disabled.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-disabled.yml
new file mode 100644
index 000000000..55ce261b5
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-disabled.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-html.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-html.yml
new file mode 100644
index 000000000..d3341080f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-html.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-plain.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-plain.yml
new file mode 100644
index 000000000..e07249390
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401-text-plain.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401.yml
new file mode 100644
index 000000000..4a9305a6d
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-401.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-with-user.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-with-user.yml
new file mode 100644
index 000000000..b461b5b24
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied-with-user.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied.yml
new file mode 100644
index 000000000..5107d15e0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-pull-project-denied.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-disabled.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-disabled.yml
new file mode 100644
index 000000000..c0617919f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-disabled.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Content-Type:
+ - application/x-www-form-urlencoded
+ response:
+ status:
+ code: 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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-html.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-html.yml
new file mode 100644
index 000000000..08dea914f
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-html.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-plain.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-plain.yml
new file mode 100644
index 000000000..46d9a1d0b
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401-text-plain.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401.yml
new file mode 100644
index 000000000..77248e2aa
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied-401.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied.yml
new file mode 100644
index 000000000..c16e608e0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/ssh-push-project-denied.yml
@@ -0,0 +1,46 @@
+---
+http_interactions:
+- request:
+ method: post
+ uri: http://localhost:3000/api/v4/internal/allowed
+ body:
+ encoding: US-ASCII
+ string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes-fail.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes-fail.yml
new file mode 100644
index 000000000..ab9b55158
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes-fail.yml
@@ -0,0 +1,46 @@
+---
+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
diff --git a/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes.yml b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes.yml
new file mode 100644
index 000000000..20eba0bc0
--- /dev/null
+++ b/ruby/vendor/gitlab-shell/spec/vcr_cassettes/two-factor-recovery-codes.yml
@@ -0,0 +1,46 @@
+---
+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