Commit e4a6499a authored by Nick Thomas's avatar Nick Thomas

Backport SSH host key detection code to CE

This functionality is needed for SSH push mirroring support, which is a
CE feature.
parent 5c2ac25d
......@@ -46,6 +46,22 @@ class Projects::MirrorsController < Projects::ApplicationController
redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
end
def ssh_host_keys
lookup = SshHostKey.new(project: project, url: params[:ssh_url])
if lookup.error.present?
# Failed to read keys
render json: { message: lookup.error }, status: :bad_request
elsif lookup.known_hosts.nil?
# Still working, come back later
render body: nil, status: :no_content
else
render json: lookup
end
rescue ArgumentError => err
render json: { message: err.message }, status: :bad_request
end
private
def remote_mirror
......
......@@ -4,22 +4,6 @@ module EE
extend ::Gitlab::Utils::Override
extend ActiveSupport::Concern
def ssh_host_keys
lookup = SshHostKey.new(project: project, url: params[:ssh_url])
if lookup.error.present?
# Failed to read keys
render json: { message: lookup.error }, status: :bad_request
elsif lookup.known_hosts.nil?
# Still working, come back later
render body: nil, status: :no_content
else
render json: lookup
end
rescue ArgumentError => err
render json: { message: err.message }, status: :bad_request
end
override :update
def update
result = ::Projects::UpdateService.new(project, current_user, safe_mirror_params).execute
......
......@@ -214,67 +214,6 @@ describe Projects::MirrorsController do
end
end
describe '#ssh_host_keys', :use_clean_rails_memory_store_caching do
let(:project) { create(:project) }
let(:cache) { SshHostKey.new(project: project, url: "ssh://example.com:22") }
before do
sign_in(project.owner)
end
context 'invalid URLs' do
where(url: %w[INVALID git@example.com:foo/bar.git ssh://git@example.com:foo/bar.git])
with_them do
it 'returns an error with a 400 response' do
do_get(project, url)
expect(response).to have_gitlab_http_status(400)
expect(json_response).to eq('message' => 'Invalid URL')
end
end
end
context 'no data in cache' do
it 'requests the cache to be filled and returns a 204 response' do
expect(ReactiveCachingWorker).to receive(:perform_async).with(cache.class, cache.id).at_least(:once)
do_get(project)
expect(response).to have_gitlab_http_status(204)
end
end
context 'error in the cache' do
it 'returns the error with a 400 response' do
stub_reactive_cache(cache, error: 'An error')
do_get(project)
expect(response).to have_gitlab_http_status(400)
expect(json_response).to eq('message' => 'An error')
end
end
context 'data in the cache' do
let(:ssh_key) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf' }
let(:ssh_fp) { { type: 'ed25519', bits: 256, fingerprint: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16', index: 0 } }
it 'returns the data with a 200 response' do
stub_reactive_cache(cache, known_hosts: ssh_key)
do_get(project)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to eq('known_hosts' => ssh_key, 'fingerprints' => [ssh_fp.stringify_keys], 'changes_project_import_data' => true)
end
end
def do_get(project, url = 'ssh://example.com')
get :ssh_host_keys, namespace_id: project.namespace, project_id: project, ssh_url: url
end
end
def do_put(project, options, extra_attrs = {})
attrs = extra_attrs.merge(namespace_id: project.namespace.to_param, project_id: project.to_param)
attrs[:project] = options
......
......@@ -63,6 +63,67 @@ describe Projects::MirrorsController do
end
end
describe '#ssh_host_keys', :use_clean_rails_memory_store_caching do
let(:project) { create(:project) }
let(:cache) { SshHostKey.new(project: project, url: "ssh://example.com:22") }
before do
sign_in(project.owner)
end
context 'invalid URLs' do
where(url: %w[INVALID git@example.com:foo/bar.git ssh://git@example.com:foo/bar.git])
with_them do
it 'returns an error with a 400 response' do
do_get(project, url)
expect(response).to have_gitlab_http_status(400)
expect(json_response).to eq('message' => 'Invalid URL')
end
end
end
context 'no data in cache' do
it 'requests the cache to be filled and returns a 204 response' do
expect(ReactiveCachingWorker).to receive(:perform_async).with(cache.class, cache.id).at_least(:once)
do_get(project)
expect(response).to have_gitlab_http_status(204)
end
end
context 'error in the cache' do
it 'returns the error with a 400 response' do
stub_reactive_cache(cache, error: 'An error')
do_get(project)
expect(response).to have_gitlab_http_status(400)
expect(json_response).to eq('message' => 'An error')
end
end
context 'data in the cache' do
let(:ssh_key) { 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf' }
let(:ssh_fp) { { type: 'ed25519', bits: 256, fingerprint: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16', index: 0 } }
it 'returns the data with a 200 response' do
stub_reactive_cache(cache, known_hosts: ssh_key)
do_get(project)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to eq('known_hosts' => ssh_key, 'fingerprints' => [ssh_fp.stringify_keys], 'changes_project_import_data' => true)
end
end
def do_get(project, url = 'ssh://example.com')
get :ssh_host_keys, namespace_id: project.namespace, project_id: project, ssh_url: url
end
end
def do_put(project, options, extra_attrs = {})
attrs = extra_attrs.merge(namespace_id: project.namespace.to_param, project_id: project.to_param)
attrs[:project] = options
......
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