From 6cf55e51292852975e8cc5df4bb54ca39e5dc727 Mon Sep 17 00:00:00 2001 From: Mark Lapierre Date: Mon, 11 Jun 2018 18:58:06 -0400 Subject: Add an SSH key and use it to clone and push Adds 2 end-to-end tests: 1. Add and remove an SSH key 2. Add an SSH key and use it to clone and push Includes changes to factories to allow Git actions via SSH --- qa/qa/factory/repository/project_push.rb | 18 ++++++++-- qa/qa/factory/repository/push.rb | 18 +++++++--- qa/qa/factory/repository/wiki_push.rb | 4 +-- qa/qa/factory/resource/project.rb | 7 ++++ qa/qa/factory/resource/ssh_key.rb | 40 ++++++++++++++++++++++ qa/qa/git/repository.rb | 37 ++++++++++++++++++-- qa/qa/page/menu/profile.rb | 7 ++++ qa/qa/page/menu/side.rb | 3 +- qa/qa/page/profile/ssh_keys.rb | 34 ++++++++++++++++++ .../3_create/repository/add_ssh_key_spec.rb | 31 +++++++++++++++++ .../3_create/repository/use_ssh_key_spec.rb | 40 ++++++++++++++++++++++ 11 files changed, 226 insertions(+), 13 deletions(-) create mode 100644 qa/qa/factory/resource/ssh_key.rb create mode 100644 qa/qa/page/profile/ssh_keys.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb create mode 100644 qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb (limited to 'qa/qa') diff --git a/qa/qa/factory/repository/project_push.rb b/qa/qa/factory/repository/project_push.rb index 4f78098d348..167f47c9141 100644 --- a/qa/qa/factory/repository/project_push.rb +++ b/qa/qa/factory/repository/project_push.rb @@ -11,7 +11,9 @@ module QA factory.output end - product(:project) { |factory| factory.project } + product :project do |factory| + factory.project + end def initialize @file_name = 'file.txt' @@ -21,8 +23,8 @@ module QA @new_branch = true end - def repository_uri - @repository_uri ||= begin + def repository_http_uri + @repository_http_uri ||= begin project.visit! Page::Project::Show.act do choose_repository_clone_http @@ -30,6 +32,16 @@ module QA end end end + + def repository_ssh_uri + @repository_ssh_uri ||= begin + project.visit! + Page::Project::Show.act do + choose_repository_clone_ssh + repository_location.uri + end + end + end end end end diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb index 5b7ebf6c41f..6c5088f1da5 100644 --- a/qa/qa/factory/repository/push.rb +++ b/qa/qa/factory/repository/push.rb @@ -5,8 +5,8 @@ module QA module Repository class Push < Factory::Base attr_accessor :file_name, :file_content, :commit_message, - :branch_name, :new_branch, :output, :repository_uri, - :user + :branch_name, :new_branch, :output, :repository_http_uri, + :repository_ssh_uri, :ssh_key, :user attr_writer :remote_branch @@ -16,7 +16,8 @@ module QA @commit_message = "This is a test commit" @branch_name = 'master' @new_branch = true - @repository_uri = "" + @repository_http_uri = "" + @ssh_key = nil end def remote_branch @@ -31,9 +32,14 @@ module QA def fabricate! Git::Repository.perform do |repository| - repository.uri = repository_uri + if ssh_key + repository.uri = repository_ssh_uri + repository.use_ssh_key(ssh_key) + else + repository.uri = repository_http_uri + repository.use_default_credentials + end - repository.use_default_credentials username = 'GitLab QA' email = 'root@gitlab.com' @@ -63,6 +69,8 @@ module QA repository.commit(commit_message) @output = repository.push_changes("#{branch_name}:#{remote_branch}") + + repository.delete_ssh_key end end end diff --git a/qa/qa/factory/repository/wiki_push.rb b/qa/qa/factory/repository/wiki_push.rb index fb7c2bb660d..ecc6cc18c88 100644 --- a/qa/qa/factory/repository/wiki_push.rb +++ b/qa/qa/factory/repository/wiki_push.rb @@ -16,8 +16,8 @@ module QA @new_branch = false end - def repository_uri - @repository_uri ||= begin + def repository_http_uri + @repository_http_uri ||= begin wiki.visit! Page::Project::Wiki::Show.act do go_to_clone_repository diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb index 7fff22b5468..90db26ab3ab 100644 --- a/qa/qa/factory/resource/project.rb +++ b/qa/qa/factory/resource/project.rb @@ -20,6 +20,13 @@ module QA end end + product :repository_http_location do + Page::Project::Show.act do + choose_repository_clone_http + repository_location + end + end + def initialize @description = 'My awesome project' end diff --git a/qa/qa/factory/resource/ssh_key.rb b/qa/qa/factory/resource/ssh_key.rb new file mode 100644 index 00000000000..6c872f32d16 --- /dev/null +++ b/qa/qa/factory/resource/ssh_key.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module QA + module Factory + module Resource + class SSHKey < Factory::Base + extend Forwardable + + attr_accessor :title + attr_reader :private_key, :public_key, :fingerprint + def_delegators :key, :private_key, :public_key, :fingerprint + + product :private_key do |factory| + factory.private_key + end + + product :title do |factory| + factory.title + end + + product :fingerprint do |factory| + factory.fingerprint + end + + def key + @key ||= Runtime::Key::RSA.new + end + + def fabricate! + Page::Menu::Main.act { go_to_profile_settings } + Page::Menu::Profile.act { click_ssh_keys } + + Page::Profile::SSHKeys.perform do |page| + page.add_key(public_key, title) + end + end + end + end + end +end diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index bdbb18b5045..a2a1d61152f 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -7,6 +7,10 @@ module QA class Repository include Scenario::Actable + def initialize + @ssh_cmd = "" + end + def self.perform(*args) Dir.mktmpdir do |dir| Dir.chdir(dir) { super } @@ -33,7 +37,7 @@ module QA end def clone(opts = '') - run_and_redact_credentials("git clone #{opts} #{@uri} ./") + run_and_redact_credentials(build_git_command("git clone #{opts} #{@uri} ./")) end def checkout(branch_name) @@ -53,6 +57,10 @@ module QA `git config user.email #{email}` end + def configure_ssh_command(command) + @ssh_cmd = "GIT_SSH_COMMAND='#{command}'" + end + def commit_file(name, contents, message) add_file(name, contents) commit(message) @@ -69,7 +77,7 @@ module QA end def push_changes(branch = 'master') - output, _ = run_and_redact_credentials("git push #{@uri} #{branch}") + output, _ = run_and_redact_credentials(build_git_command("git push #{@uri} #{branch}")) output end @@ -78,6 +86,31 @@ module QA `git log --oneline`.split("\n") end + def use_ssh_key(key) + @private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}") + File.binwrite(@private_key_file, key.private_key) + File.chmod(0700, @private_key_file) + + @known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}") + keyscan_params = ['-H'] + keyscan_params << "-p #{@uri.port}" if @uri.port + keyscan_params << @uri.host + run_and_redact_credentials("ssh-keyscan #{keyscan_params.join(' ')} >> #{@known_hosts_file.path}") + + configure_ssh_command("ssh -i #{@private_key_file.path} -o UserKnownHostsFile=#{@known_hosts_file.path}") + end + + def delete_ssh_key + return unless @private_key_file + + @private_key_file.close(true) + @known_hosts_file.close(true) + end + + def build_git_command(command_str) + [@ssh_cmd, command_str].compact.join(' ') + end + private # Since the remote URL contains the credentials, and git occasionally diff --git a/qa/qa/page/menu/profile.rb b/qa/qa/page/menu/profile.rb index 95e88d863e4..7e24fa85c33 100644 --- a/qa/qa/page/menu/profile.rb +++ b/qa/qa/page/menu/profile.rb @@ -6,6 +6,7 @@ module QA element :access_token_link, 'link_to profile_personal_access_tokens_path' element :access_token_title, 'Access Tokens' element :top_level_items, '.sidebar-top-level-items' + element :ssh_keys, 'SSH Keys' end def click_access_tokens @@ -14,6 +15,12 @@ module QA end end + def click_ssh_keys + within_sidebar do + click_link('SSH Keys') + end + end + private def within_sidebar diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb index 354ccec2a5a..a1eedfea42e 100644 --- a/qa/qa/page/menu/side.rb +++ b/qa/qa/page/menu/side.rb @@ -6,6 +6,7 @@ module QA element :settings_item element :settings_link, 'link_to edit_project_path' element :repository_link, "title: _('Repository')" + element :link_pipelines element :pipelines_settings_link, "title: _('CI / CD')" element :operations_kubernetes_link, "title: _('Kubernetes')" element :issues_link, /link_to.*shortcuts-issues/ @@ -49,7 +50,7 @@ module QA def click_ci_cd_pipelines within_sidebar do - click_link('CI / CD') + click_element :link_pipelines end end diff --git a/qa/qa/page/profile/ssh_keys.rb b/qa/qa/page/profile/ssh_keys.rb new file mode 100644 index 00000000000..ce1813b14d0 --- /dev/null +++ b/qa/qa/page/profile/ssh_keys.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module QA + module Page + module Profile + class SSHKeys < Page::Base + view 'app/views/profiles/keys/_form.html.haml' do + element :key_title_field + element :key_public_key_field + element :add_key_button + end + + view 'app/views/profiles/keys/_key_details.html.haml' do + element :delete_key_button + end + + def add_key(public_key, title) + fill_element :key_public_key_field, public_key + fill_element :key_title_field, title + + click_element :add_key_button + end + + def remove_key(title) + click_link(title) + + accept_alert do + click_element :delete_key_button + end + end + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb new file mode 100644 index 00000000000..84f663c4866 --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_ssh_key_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'SSH keys support' do + let(:key_title) { "key for ssh tests #{Time.now.to_f}" } + + it 'user adds and then removes an SSH key' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + key = Factory::Resource::SSHKey.fabricate! do |resource| + resource.title = key_title + end + + expect(page).to have_content("Title: #{key_title}") + expect(page).to have_content(key.fingerprint) + + Page::Menu::Main.act { go_to_profile_settings } + Page::Menu::Profile.act { click_ssh_keys } + + Page::Profile::SSHKeys.perform do |ssh_keys| + ssh_keys.remove_key(key_title) + end + + expect(page).not_to have_content("Title: #{key_title}") + expect(page).not_to have_content(key.fingerprint) + end + end + end +end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb new file mode 100644 index 00000000000..7c989bfd8cc --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/repository/use_ssh_key_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module QA + context :create do + describe 'SSH key support' do + # Note: If you run this test against GDK make sure you've enabled sshd + # See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md + + let(:key_title) { "key for ssh tests #{Time.now.to_f}" } + + it 'user adds an ssh key and pushes code to the repository' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + key = Factory::Resource::SSHKey.fabricate! do |resource| + resource.title = key_title + end + + Factory::Repository::ProjectPush.fabricate! do |push| + push.ssh_key = key + push.file_name = 'README.md' + push.file_content = '# Test Use SSH Key' + push.commit_message = 'Add README.md' + end + + Page::Project::Show.act { wait_for_push } + + expect(page).to have_content('README.md') + expect(page).to have_content('Test Use SSH Key') + + Page::Menu::Main.act { go_to_profile_settings } + Page::Menu::Profile.act { click_ssh_keys } + + Page::Profile::SSHKeys.perform do |ssh_keys| + ssh_keys.remove_key(key_title) + end + end + end + end +end -- cgit v1.2.1