summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorIgor <idrozdov@gitlab.com>2019-03-21 11:53:09 +0000
committerNick Thomas <nick@gitlab.com>2019-03-21 11:53:09 +0000
commit98dbdfb758703428626d54b2a257565a44509a55 (patch)
treea3fdc408786fd0342bd3eb28ad841e70d3d7ac6e /spec
parent81bed658f083a165e65b16f7ef86c18938349e33 (diff)
downloadgitlab-shell-98dbdfb758703428626d54b2a257565a44509a55.tar.gz
Provide go implementation for 2fa_recovery_codes command
Diffstat (limited to 'spec')
-rw-r--r--spec/gitlab_shell_authorized_keys_check_spec.rb45
-rw-r--r--spec/gitlab_shell_gitlab_shell_spec.rb66
-rw-r--r--spec/gitlab_shell_two_factor_recovery_spec.rb128
-rw-r--r--spec/support/gitlab_shell_setup.rb58
4 files changed, 203 insertions, 94 deletions
diff --git a/spec/gitlab_shell_authorized_keys_check_spec.rb b/spec/gitlab_shell_authorized_keys_check_spec.rb
index baaa560..7050604 100644
--- a/spec/gitlab_shell_authorized_keys_check_spec.rb
+++ b/spec/gitlab_shell_authorized_keys_check_spec.rb
@@ -1,20 +1,7 @@
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
+ include_context 'gitlab shell'
def tmp_socket_path
# This has to be a relative path shorter than 100 bytes due to
@@ -22,12 +9,8 @@ describe 'bin/gitlab-shell-authorized-keys-check' do
'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|
+ def mock_server(server)
+ 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'
@@ -36,28 +19,14 @@ describe 'bin/gitlab-shell-authorized-keys-check' do
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)
+ before(:all) do
+ write_config(
+ "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_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
diff --git a/spec/gitlab_shell_gitlab_shell_spec.rb b/spec/gitlab_shell_gitlab_shell_spec.rb
index cb3fd9c..6d6e172 100644
--- a/spec/gitlab_shell_gitlab_shell_spec.rb
+++ b/spec/gitlab_shell_gitlab_shell_spec.rb
@@ -3,33 +3,10 @@ require_relative 'spec_helper'
require 'open3'
describe 'bin/gitlab-shell' 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-socket'
- end
-
- before(:all) do
- FileUtils.mkdir_p(File.dirname(tmp_socket_path))
- FileUtils.touch(File.join(tmp_root_path, '.gitlab_shell_secret'))
+ include_context 'gitlab shell'
- @server = HTTPUNIXServer.new(BindAddress: tmp_socket_path)
- @server.mount_proc('/api/v4/internal/discover') do |req, res|
+ def mock_server(server)
+ server.mount_proc('/api/v4/internal/discover') do |req, res|
identifier = req.query['key_id'] || req.query['username'] || req.query['user_id']
known_identifiers = %w(10 someuser 100)
if known_identifiers.include?(identifier)
@@ -47,24 +24,16 @@ describe 'bin/gitlab-shell' do
res.status = 500
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
- system(original_root_path, 'bin/compile')
- 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
+ def run!(args, env: {'SSH_CONNECTION' => 'fake'})
+ cmd = [
+ gitlab_shell_path,
+ args
+ ].flatten.compact.join(' ')
- let(:gitlab_shell_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell') }
+ Open3.capture3(env, cmd)
+ end
shared_examples 'results with keys' do
# Basic valid input
@@ -175,19 +144,4 @@ describe 'bin/gitlab-shell' do
expect(status).not_to be_success
end
end
-
- def run!(args, env: {'SSH_CONNECTION' => 'fake'})
- cmd = [
- gitlab_shell_path,
- args
- ].flatten.compact.join(' ')
-
- Open3.capture3(env, cmd)
- end
-
- def write_config(config)
- File.open(config_path, 'w') do |f|
- f.write(config.to_yaml)
- end
- end
end
diff --git a/spec/gitlab_shell_two_factor_recovery_spec.rb b/spec/gitlab_shell_two_factor_recovery_spec.rb
new file mode 100644
index 0000000..19999e5
--- /dev/null
+++ b/spec/gitlab_shell_two_factor_recovery_spec.rb
@@ -0,0 +1,128 @@
+require_relative 'spec_helper'
+
+require 'open3'
+
+describe 'bin/gitlab-shell 2fa_recovery_codes' do
+ include_context 'gitlab shell'
+
+ def mock_server(server)
+ server.mount_proc('/api/v4/internal/two_factor_recovery_codes') do |req, res|
+ res.content_type = 'application/json'
+ res.status = 200
+
+ key_id = req.query['key_id'] || req.query['user_id']
+
+ unless key_id
+ body = JSON.parse(req.body)
+ key_id = body['key_id'] || body['user_id'].to_s
+ end
+
+ if key_id == '100'
+ res.body = '{"success":true, "recovery_codes": ["1", "2"]}'
+ else
+ res.body = '{"success":false, "message": "Forbidden!"}'
+ end
+ end
+
+ server.mount_proc('/api/v4/internal/discover') do |req, res|
+ res.status = 200
+ res.content_type = 'application/json'
+ res.body = '{"id":100, "name": "Some User", "username": "someuser"}'
+ end
+ end
+
+ shared_examples 'dialog for regenerating recovery keys' do
+ context 'when the user agrees to regenerate keys' do
+ def verify_successful_regeneration!(cmd)
+ Open3.popen2(env, cmd) do |stdin, stdout|
+ expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
+ expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")
+
+ stdin.puts('yes')
+
+ expect(stdout.flush.read).to eq(
+ "\nYour two-factor authentication recovery codes are:\n\n" \
+ "1\n2\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.\n"
+ )
+ end
+ end
+
+ context 'when key is provided' do
+ let(:cmd) { "#{gitlab_shell_path} key-100" }
+
+ it 'the recovery keys are regenerated' do
+ verify_successful_regeneration!(cmd)
+ end
+ end
+
+ context 'when username is provided' do
+ let(:cmd) { "#{gitlab_shell_path} username-someone" }
+
+ it 'the recovery keys are regenerated' do
+ verify_successful_regeneration!(cmd)
+ end
+ end
+ end
+
+ context 'when the user disagrees to regenerate keys' do
+ let(:cmd) { "#{gitlab_shell_path} key-100" }
+
+ it 'the recovery keys are not regenerated' do
+ Open3.popen2(env, cmd) do |stdin, stdout|
+ expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
+ expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")
+
+ stdin.puts('no')
+
+ expect(stdout.flush.read).to eq(
+ "\nNew recovery codes have *not* been generated. Existing codes will remain valid.\n"
+ )
+ end
+ end
+ end
+
+ context 'when API error occurs' do
+ let(:cmd) { "#{gitlab_shell_path} key-101" }
+
+ context 'when the user agrees to regenerate keys' do
+ it 'the recovery keys are regenerated' do
+ Open3.popen2(env, cmd) do |stdin, stdout|
+ expect(stdout.gets).to eq("Are you sure you want to generate new two-factor recovery codes?\n")
+ expect(stdout.gets).to eq("Any existing recovery codes you saved will be invalidated. (yes/no)\n")
+
+ stdin.puts('yes')
+
+ expect(stdout.flush.read).to eq("\nAn error occurred while trying to generate new recovery codes.\nForbidden!\n")
+ end
+ end
+ end
+ end
+ end
+
+ let(:env) { {'SSH_CONNECTION' => 'fake', 'SSH_ORIGINAL_COMMAND' => '2fa_recovery_codes' } }
+
+ describe 'without go features' do
+ before(:context) do
+ write_config(
+ "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
+ )
+ end
+
+ it_behaves_like 'dialog for regenerating recovery keys'
+ end
+
+ describe 'with go features' do
+ before(:context) do
+ write_config(
+ "gitlab_url" => "http+unix://#{CGI.escape(tmp_socket_path)}",
+ "migration" => { "enabled" => true,
+ "features" => ["2fa_recovery_codes"] }
+ )
+ end
+
+ it_behaves_like 'dialog for regenerating recovery keys'
+ end
+end
diff --git a/spec/support/gitlab_shell_setup.rb b/spec/support/gitlab_shell_setup.rb
new file mode 100644
index 0000000..eddd2d1
--- /dev/null
+++ b/spec/support/gitlab_shell_setup.rb
@@ -0,0 +1,58 @@
+RSpec.shared_context 'gitlab shell', shared_context: :metadata do
+ def original_root_path
+ ROOT_PATH
+ end
+
+ def config_path
+ File.join(tmp_root_path, 'config.yml')
+ end
+
+ def write_config(config)
+ File.open(config_path, 'w') do |f|
+ f.write(config.to_yaml)
+ end
+ end
+
+ def tmp_root_path
+ @tmp_root_path ||= File.realpath(Dir.mktmpdir)
+ end
+
+ def mock_server(server)
+ raise NotImplementedError.new(
+ 'mock_server method must be implemented in order to include gitlab shell context'
+ )
+ end
+
+ # This has to be a relative path shorter than 100 bytes due to
+ # limitations in how Unix sockets work.
+ def tmp_socket_path
+ 'tmp/gitlab-shell-socket'
+ end
+
+ let(:gitlab_shell_path) { File.join(tmp_root_path, 'bin', 'gitlab-shell') }
+
+ 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)
+
+ mock_server(@server)
+
+ @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
+ system(original_root_path, 'bin/compile')
+
+ 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
+end