Commit ab72b0b6 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'qa-add-more-key-tests' into 'master'

Add more QA SSH key tests

Closes gitlab-qa#183

See merge request gitlab-org/gitlab-ce!17662
parents df51cf69 6c21b502
......@@ -17,14 +17,14 @@
.ci-variable-row-body
%input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id }
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
%input.js-ci-variable-input-key.ci-variable-body-item.form-control{ type: "text",
%input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text",
name: key_input_name,
value: key,
placeholder: s_('CiVariables|Input variable key') }
.ci-variable-body-item
.form-control.js-secret-value-placeholder{ class: ('hide' unless id) }
.form-control.js-secret-value-placeholder.qa-ci-variable-input-value{ class: ('hide' unless id) }
= '*' * 20
%textarea.js-ci-variable-input-value.js-secret-value.form-control{ class: ('hide' if id),
%textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ class: ('hide' if id),
rows: 1,
name: value_input_name,
placeholder: s_('CiVariables|Input variable value') }
......
FROM ruby:2.4
FROM ruby:2.4-stretch
LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive
......
......@@ -6,5 +6,4 @@ gem 'capybara-screenshot', '~> 1.0.18'
gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.8.0'
gem 'net-ssh', require: false
gem 'airborne', '~> 0.2.13'
......@@ -46,7 +46,6 @@ GEM
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.1)
net-ssh (4.1.0)
netrc (0.11.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
......@@ -98,7 +97,6 @@ DEPENDENCIES
airborne (~> 0.2.13)
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
net-ssh
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
......
......@@ -11,9 +11,15 @@ module QA
autoload :Scenario, 'qa/runtime/scenario'
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
autoload :RSAKey, 'qa/runtime/rsa_key'
autoload :Address, 'qa/runtime/address'
autoload :API, 'qa/runtime/api'
module Key
autoload :Base, 'qa/runtime/key/base'
autoload :RSA, 'qa/runtime/key/rsa'
autoload :ECDSA, 'qa/runtime/key/ecdsa'
autoload :ED25519, 'qa/runtime/key/ed25519'
end
end
##
......
......@@ -2,7 +2,8 @@ module QA
module Factory
module Repository
class Push < Factory::Base
attr_writer :file_name, :file_content, :commit_message, :branch_name, :new_branch
attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :remote_branch
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-code'
......@@ -17,23 +18,32 @@ module QA
@new_branch = true
end
def remote_branch
@remote_branch ||= branch_name
end
def fabricate!
project.visit!
Git::Repository.perform do |repository|
repository.location = Page::Project::Show.act do
repository.uri = Page::Project::Show.act do
choose_repository_clone_http
repository_location
repository_location.uri
end
repository.use_default_credentials
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
repository.checkout(@branch_name) unless @new_branch
repository.add_file(@file_name, @file_content)
repository.commit(@commit_message)
repository.push_changes(@branch_name)
if new_branch
repository.checkout_new_branch(branch_name)
else
repository.checkout(branch_name)
end
repository.add_file(file_name, file_content)
repository.commit(commit_message)
repository.push_changes("#{branch_name}:#{remote_branch}")
end
end
end
......
......@@ -39,7 +39,9 @@ module QA
resource.project = project
resource.file_name = 'README.md'
resource.commit_message = 'Add readme'
resource.branch_name = "master:#{@branch_name}"
resource.branch_name = 'master'
resource.new_branch = false
resource.remote_branch = @branch_name
end
Page::Project::Show.act { wait_for_push }
......
......@@ -4,15 +4,15 @@ module QA
class DeployKey < Factory::Base
attr_accessor :title, :key
product :title do
product :fingerprint do |resource|
Page::Project::Settings::Repository.act do
expand_deploy_keys(&:key_title)
end
end
expand_deploy_keys do |key|
key_offset = key.key_titles.index do |title|
title.text == resource.title
end
product :fingerprint do
Page::Project::Settings::Repository.act do
expand_deploy_keys(&:key_fingerprint)
key.key_fingerprints[key_offset].text
end
end
end
......
......@@ -24,12 +24,14 @@ module QA
dependency Factory::Repository::Push, as: :target do |push, factory|
factory.project.visit!
push.project = factory.project
push.branch_name = "master:#{factory.target_branch}"
push.branch_name = 'master'
push.remote_branch = factory.target_branch
end
dependency Factory::Repository::Push, as: :source do |push, factory|
push.project = factory.project
push.branch_name = "#{factory.target_branch}:#{factory.source_branch}"
push.branch_name = factory.target_branch
push.remote_branch = factory.source_branch
push.file_name = "added_file.txt"
push.file_content = "File Added"
end
......
......@@ -17,6 +17,13 @@ module QA
Page::Project::Show.act { project_name }
end
product :repository_ssh_location do
Page::Project::Show.act do
choose_repository_clone_ssh
repository_location
end
end
def fabricate!
group.visit!
......
......@@ -16,8 +16,7 @@ module QA
Page::Project::Settings::CICD.perform do |setting|
setting.expand_secret_variables do |page|
page.fill_variable_key(key)
page.fill_variable_value(value)
page.fill_variable(key, value)
page.save_variables
end
......
......@@ -14,7 +14,7 @@ module QA
def initialize(git_uri)
@git_uri = git_uri
@uri =
if git_uri.start_with?('ssh://')
if git_uri =~ %r{\A(?:ssh|http|https)://}
URI.parse(git_uri)
else
*rest, path = git_uri.split(':')
......
......@@ -15,8 +15,7 @@ module QA
end
end
def location=(address)
@location = address
def uri=(address)
@uri = URI(address)
end
......@@ -43,6 +42,10 @@ module QA
`git checkout "#{branch_name}"`
end
def checkout_new_branch(branch_name)
`git checkout -b "#{branch_name}"`
end
def shallow_clone
clone('--depth 1')
end
......
......@@ -64,6 +64,10 @@ module QA
find(element_selector_css(name))
end
def all_elements(name)
all(element_selector_css(name))
end
def click_element(name)
find_element(name).click
end
......
......@@ -42,6 +42,18 @@ module QA
end
end
def key_titles
within_project_deploy_keys do
all_elements(:key_title)
end
end
def key_fingerprints
within_project_deploy_keys do
all_elements(:key_fingerprint)
end
end
private
def within_project_deploy_keys
......
......@@ -7,10 +7,8 @@ module QA
view 'app/views/ci/variables/_variable_row.html.haml' do
element :variable_row, '.ci-variable-row-body'
element :variable_key, '.js-ci-variable-input-key'
element :variable_value, '.js-ci-variable-input-value'
element :key_placeholder, 'Input variable key'
element :value_placeholder, 'Input variable value'
element :variable_key, '.qa-ci-variable-input-key'
element :variable_value, '.qa-ci-variable-input-value'
end
view 'app/views/ci/variables/_index.html.haml' do
......@@ -18,12 +16,14 @@ module QA
element :reveal_values, '.js-secret-value-reveal-button'
end
def fill_variable_key(key)
fill_in('Input variable key', with: key, match: :first)
end
def fill_variable(key, value)
keys = all_elements(:ci_variable_input_key)
index = keys.size - 1
def fill_variable_value(value)
fill_in('Input variable value', with: value, match: :first)
# After we fill the key, JS would generate another field so
# we need to use the same index to find the corresponding one.
keys[index].set(key)
all_elements(:ci_variable_input_value)[index].set(value)
end
def save_variables
......@@ -36,7 +36,7 @@ module QA
def variable_value(key)
within('.ci-variable-row-body', text: key) do
find('.js-ci-variable-input-value').value
find('.qa-ci-variable-input-value').value
end
end
end
......
......@@ -38,11 +38,7 @@ module QA
end
def repository_location
find('#project_clone').value
end
def repository_location_uri
Git::Location.new(repository_location)
Git::Location.new(find('#project_clone').value)
end
def project_name
......@@ -91,7 +87,7 @@ module QA
end
# Ensure git clone textbox was updated
repository_location.include?(detect_text)
repository_location.git_uri.include?(detect_text)
end
end
end
......
module QA
module Runtime
module Key
class Base
attr_reader :name, :bits, :private_key, :public_key, :fingerprint
def initialize(name, bits)
@name = name
@bits = bits
Dir.mktmpdir do |dir|
path = "#{dir}/id_#{name}"
ssh_keygen(name, bits, path)
populate_key_data(path)
end
end
private
def ssh_keygen(name, bits, path)
cmd = %W[ssh-keygen -t #{name} -b #{bits} -f #{path} -N] << ''
Service::Shellout.shell(cmd)
end
def populate_key_data(path)
@private_key = File.binread(path)
@public_key = File.binread("#{path}.pub")
@fingerprint =
`ssh-keygen -l -E md5 -f #{path} | cut -d' ' -f2 | cut -d: -f2-`.chomp
end
end
end
end
end
module QA
module Runtime
module Key
class ECDSA < Base
def initialize(bits = 521)
super('ecdsa', bits)
end
end
end
end
end
module QA
module Runtime
module Key
class ED25519 < Base
def initialize
super('ed25519', 256)
end
end
end
end
end
module QA
module Runtime
module Key
class RSA < Base
def initialize(bits = 4096)
super('rsa', bits)
end
end
end
end
end
require 'net/ssh'
require 'forwardable'
module QA
module Runtime
class RSAKey
extend Forwardable
attr_reader :key
def_delegators :@key, :fingerprint, :to_pem
def initialize(bits = 4096)
@key = OpenSSL::PKey::RSA.new(bits)
end
def public_key
@public_key ||= "#{key.ssh_type} #{[key.to_blob].pack('m0')}"
end
end
end
end
......@@ -5,6 +5,8 @@ module QA
module Shellout
CommandError = Class.new(StandardError)
module_function
##
# TODO, make it possible to use generic QA framework classes
# as a library - gitlab-org/gitlab-qa#94
......@@ -12,7 +14,7 @@ module QA
def shell(command)
puts "Executing `#{command}`"
Open3.popen2e(command) do |_in, out, wait|
Open3.popen2e(*command) do |_in, out, wait|
out.each { |line| puts line }
if wait.value.exited? && wait.value.exitstatus.nonzero?
......
......@@ -4,7 +4,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
key = Runtime::RSAKey.new
key = Runtime::Key::RSA.new
deploy_key_title = 'deploy key title'
deploy_key_value = key.public_key
......@@ -13,7 +13,6 @@ module QA
resource.key = deploy_key_value
end
expect(deploy_key.title).to eq(deploy_key_title)
expect(deploy_key.fingerprint).to eq(key.fingerprint)
end
end
......
......@@ -2,79 +2,103 @@ require 'digest/sha1'
module QA
feature 'cloning code using a deploy key', :core, :docker do
let(:runner_name) { "qa-runner-#{Time.now.to_i}" }
let(:key) { Runtime::RSAKey.new }
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
given(:project) do
Factory::Resource::Project.fabricate! do |resource|
before(:all) do
login
@runner_name = "qa-runner-#{Time.now.to_i}"
@project = Factory::Resource::Project.fabricate! do |resource|
resource.name = 'deploy-key-clone-project'
end
end
after do
Service::Runner.new(runner_name).remove!
end
scenario 'user sets up a deploy key to clone code using pipelines' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
@repository_location = @project.repository_ssh_location
Factory::Resource::Runner.fabricate! do |resource|
resource.project = project
resource.name = runner_name
resource.project = @project
resource.name = @runner_name
resource.tags = %w[qa docker]
resource.image = 'gitlab/gitlab-runner:ubuntu'
end
Factory::Resource::DeployKey.fabricate! do |resource|
resource.project = project
resource.title = 'deploy key title'
resource.key = key.public_key
end
Page::Menu::Main.act { sign_out }
end
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.project = project
resource.key = 'DEPLOY_KEY'
resource.value = key.to_pem
end
after(:all) do
Service::Runner.new(@runner_name).remove!
end
project.visit!
keys = [
Runtime::Key::RSA.new(8192),
Runtime::Key::ECDSA.new(521),
Runtime::Key::ED25519.new
]
repository_uri = Page::Project::Show.act do
choose_repository_clone_ssh
repository_location_uri
end
keys.each do |key|
scenario "user sets up a deploy key with #{key.name}(#{key.bits}) to clone code using pipelines" do
login
gitlab_ci = <<~YAML
cat-config:
script:
- mkdir -p ~/.ssh
- ssh-keyscan -p #{repository_uri.port} #{repository_uri.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s)
- echo "$DEPLOY_KEY" | ssh-add -
- git clone #{repository_uri.git_uri}
- sha1sum #{project.name}/.gitlab-ci.yml
tags:
- qa
- docker
YAML
Factory::Repository::Push.fabricate! do |resource|
resource.project = project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
resource.file_content = gitlab_ci
end
Factory::Resource::DeployKey.fabricate! do |resource|
resource.project = @project
resource.title = "deploy key #{key.name}(#{key.bits})"
resource.key = key.public_key
end
deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}"
Factory::Resource::SecretVariable.fabricate! do |resource|
resource.project = @project
resource.key = deploy_key_name
resource.value = key.private_key
end
gitlab_ci = <<~YAML
cat-config:
script:
- mkdir -p ~/.ssh
- ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts
- eval $(ssh-agent -s)
- ssh-add -D
- echo "$#{deploy_key_name}" | ssh-add -
- git clone #{@repository_location.git_uri}
- cd #{@project.name}
- git checkout #{deploy_key_name}
- sha1sum .gitlab-ci.yml
tags:
- qa
- docker
YAML
Factory::Repository::Push.fabricate! do |resource|
resource.project = @project
resource.file_name = '.gitlab-ci.yml'
resource.commit_message = 'Add .gitlab-ci.yml'
resource.file_content = gitlab_ci
resource.branch_name = deploy_key_name
resource.new_branch = true
end
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Show.act { wait_for_push }
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
Page::Project::Pipeline::Show.act do
go_to_first_job
Page::Project::Show.act { wait_for_push }
Page::Menu::Side.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
Page::Project::Pipeline::Show.act { go_to_first_job }
wait do
!has_content?('running')
end
end
Page::Project::Job::Show.perform do |job|
expect(job.output).to include(sha1sum)
Page::Project::Job::Show.perform do |job|
expect(job.output).to include(sha1sum)
end
end
end
end
......
......@@ -18,7 +18,7 @@ module QA
end
Git::Repository.perform do |repository|
repository.location = location
repository.uri = location.uri
repository.use_default_credentials
repository.act do
......@@ -33,7 +33,7 @@ module QA
scenario 'user performs a deep clone' do
Git::Repository.perform do |repository|
repository.location = location
repository.uri = location.uri
repository.use_default_credentials
repository.act { clone }
......@@ -44,7 +44,7 @@ module QA
scenario 'user performs a shallow clone' do
Git::Repository.perform do |repository|
repository.location = location
repository.uri = location.uri
repository.use_default_credentials
repository.act { shallow_clone }
......
......@@ -42,7 +42,7 @@ module QA
project.visit!
Git::Repository.perform do |repository|
repository.location = location
repository.uri = location.uri
repository.use_default_credentials
repository.act do
......
describe QA::Runtime::Key::ECDSA do
describe '#public_key' do
[256, 384, 521].each do |bits|
it "generates a public #{bits}-bits ECDSA key" do
subject = described_class.new(bits).public_key
expect(subject).to match(%r{\Aecdsa\-sha2\-\w+ AAAA[0-9A-Za-z+/]+={0,3}})
end
end
end
describe '#new' do
it 'does not support arbitrary bits' do
expect { described_class.new(123) }
.to raise_error(QA::Service::Shellout::CommandError)
end
end
end
describe QA::Runtime::Key::ED25519 do
describe '#public_key' do
subject { described_class.new.public_key }
it 'generates a public ED25519 key' do
expect(subject).to match(%r{\Assh\-ed25519 AAAA[0-9A-Za-z+/]})
end
end
end
describe QA::Runtime::RSAKey do
describe QA::Runtime::Key::RSA do
describe '#public_key' do
subject { described_class.new.public_key }
it 'generates a public RSA key' do
expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}\z})
expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}})
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment