Bring back ssh key endpoint, but make it configurable

This commit is contained in:
Piotr Sarnacki 2014-07-29 03:02:54 +02:00
parent f57d4eb083
commit d7c6edec18
5 changed files with 183 additions and 0 deletions

View File

@ -115,6 +115,9 @@ module Travis::Api
use Travis::Api::App::Middleware::Rewrite
SettingsEndpoint.subclass :env_vars
if Travis.config.endpoints.ssh_key
SingletonSettingsEndpoint.subclass :ssh_key
end
Endpoint.subclasses.each do |e|
next if e == SettingsEndpoint # TODO: add something like abstract? method to check if

View File

@ -26,6 +26,7 @@ module Travis
require 'travis/api/v2/http/env_vars'
require 'travis/api/v2/http/user'
require 'travis/api/v2/http/validation_error'
require 'travis/api/v2/http/ssh_key'
end
end
end

View File

@ -0,0 +1,27 @@
require 'openssl'
module Travis
module Api
module V2
module Http
class SshKey < Travis::Api::Serializer
attributes :id, :description, :fingerprint
def id
object.repository_id
end
def fingerprint
value = object.value.decrypt
return unless value
key = OpenSSL::PKey::RSA.new(value)
ssh_rsa = "\x00\x00\x00\x07ssh-rsa" + key.e.to_s(0) + key.n.to_s(0)
OpenSSL::Digest::MD5.new(ssh_rsa).hexdigest.scan(/../).join(':')
rescue OpenSSL::PKey::RSAError
nil
end
end
end
end
end
end

View File

@ -0,0 +1,122 @@
require 'spec_helper'
describe 'ssh keys endpoint' do
let(:repo) { Factory(:repository) }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
describe 'without an authenticated user' do
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
let(:user) { Factory(:user) }
describe 'GET /ssh_key' do
it 'responds with 401' do
response = get "/settings/ssh_key/#{repo.id}", {}, headers
response.should_not be_successful
response.status.should == 401
end
end
describe 'PATCH /ssh_key' do
it 'responds with 401' do
response = patch "/settings/ssh_key/#{repo.id}", {}, headers
response.should_not be_successful
response.status.should == 401
end
end
describe 'DELETE /ssh_key' do
it 'responds with 401' do
response = delete "/settings/ssh_key/#{repo.id}", {}, headers
response.should_not be_successful
response.status.should == 401
end
end
end
describe 'with authenticated user' do
let(:user) { Factory(:user) }
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) }
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json', 'HTTP_AUTHORIZATION' => "token #{token}" } }
before { user.permissions.create!(:repository_id => repo.id, :admin => true, :push => true) }
describe 'GET /ssh_key' do
it 'returns an item' do
settings = repo.settings
record = settings.create(:ssh_key, description: 'key for my repo', value: TEST_PRIVATE_KEY)
settings.save
response = get "/settings/ssh_key/#{repo.id}", {}, headers
json = JSON.parse(response.body)
json['ssh_key']['description'].should == 'key for my repo'
json['ssh_key']['id'].should == repo.id
json['ssh_key'].should_not have_key('value')
json['ssh_key']['fingerprint'].should == '57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40'
end
it 'returns 404 if ssh_key can\'t be found' do
response = get "/settings/ssh_key/#{repo.id}", {}, headers
json = JSON.parse(response.body)
json['error'].should == "Could not find a requested setting"
end
end
describe 'PATCH /settings/ssh_key' do
it 'should update a key' do
settings = repo.settings
ssh_key = settings.create(:ssh_key, description: 'foo', value: TEST_PRIVATE_KEY)
settings.save
new_key = OpenSSL::PKey::RSA.generate(2048).to_s
body = { ssh_key: { description: 'bar', value: new_key } }.to_json
response = patch "/settings/ssh_key/#{repo.id}", body, headers
json = JSON.parse(response.body)
json['ssh_key']['description'].should == 'bar'
json['ssh_key'].should_not have_key('value')
updated_ssh_key = repo.reload.settings.ssh_key
updated_ssh_key.description.should == 'bar'
updated_ssh_key.repository_id.should == repo.id
updated_ssh_key.value.decrypt.should == new_key
end
it 'returns an error message if ssh_key is invalid' do
settings = repo.settings
ssh_key = settings.create(:ssh_key, description: 'foo', value: 'the key')
settings.save
body = { ssh_key: { value: nil } }.to_json
response = patch "/settings/ssh_key/#{repo.id}", body, headers
response.status.should == 422
json = JSON.parse(response.body)
json['message'].should == 'Validation failed'
json['errors'].should == [{
'field' => 'value',
'code' => 'missing_field'
}]
ssh_key = repo.reload.settings.ssh_key
ssh_key.description.should == 'foo'
ssh_key.repository_id.should == repo.id
ssh_key.value.decrypt.should == 'the key'
end
end
describe 'DELETE /ssh_keys/:id' do
it 'should nullify an ssh_key' do
settings = repo.settings
ssh_key = settings.create(:ssh_key, description: 'foo', value: 'the key')
settings.save
response = delete "/settings/ssh_key/#{repo.id}", {}, headers
json = JSON.parse(response.body)
json['ssh_key']['description'].should == 'foo'
json['ssh_key'].should_not have_key('value')
repo.reload.settings.ssh_key.should be_nil
end
end
end
end

View File

@ -18,6 +18,7 @@ require 'support/formats'
Travis.logger = Logger.new(StringIO.new)
Travis::Api::App.setup
Travis.config.client_domain = "www.example.com"
Travis.config.endpoints.ssh_key = true
module TestHelpers
include Sinatra::TestHelpers
@ -73,3 +74,32 @@ RSpec.configure do |c|
end
end
end
TEST_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA6Dm1n+fc0ILeLWeiwqsWs1MZaGAfccrmpvuxfcE9UaJp2POy
079g+mdiBgtWfnQlU84YX31rU2x9GJwnb8G6UcvkEjqczOgHHmELtaNmrRH1g8qO
fJpzXB8XiNib1L3TDs7qYMKLDCbl2bWrcO7Dol9bSqIeb7f9rzkCd4tuXObL3pMD
/VIW5uzeVqLBAc0Er+qw6U7clnMnHHMekXt4JSRfauSCxktR2FzigoQbJc8t4iWO
rmNi5Q84VkXB3X7PO/eajUw+RJOl6FnPN1Zh08ceqcqmSMM4RzeVQaczXg7P92P4
mRF41R97jIJyzUGwheb2Z4Q2rltck4V7R5BvMwIDAQABAoIBAE4O3+MRH+MiqiXe
+RGwSqAaZab08Hzic+dbIQ0hQEhJbITVXZ3ZbXKd/5ACjZ9R0R47X2vxj3rqM55r
FsJ0/vjxrQcHlp81uvbWLgZvF1tDdyBGnOB7Vh14AgQoszCuYdxPZu8BVZXPGWG1
tBvw1eelX91VYx+wW+BjLFYckws8kPCPY6WEnng0wQGShGqyTOJa1T4M1ethHYF+
ddNx+fVLkEf2vL59popuJMOAyVa1jvU7D3VZ67qhlxWAvQxZeEP0vFZHeWPjvRF1
orxiGuwLCG+Rgq1XSVJjMNf1qE3gZTlDg+u3ORKbRx2xlhiqpkHxLx7QtCmELwtD
Dqvf8ukCgYEA/SoQwyfMp4t19FLI4tV0rp3Yn7ZSAqRtMVrLVAIQoJzDXv9BaJFS
xb6enxRAjy+Rg10H8ijh8Z9Z3a4g3JViHQsWMrf9rL2/7M07vraIUIQoVo7yTeGa
MXnTuKmBZFGEAM9CzqAVao1Om10TRFNLgiLAU3ZEFi8J1DYWkhzrJp0CgYEA6tOa
V15MP3sJSlOTszshXKbwf6iXfjHbdpGUXmd9X3AMzOvl/CEGS2q46lwJISubHWKF
BOKk1thumM4Zu6dx89hLEoXhFycgUV/KJYl54ZfhY079Ri7SZUYIqDR04BRJC2d6
mO16Y//UwqgTaZ/lS/S791iWPTjVNEgSlRbQHA8CgYALiOEeoy+V6qrDKQpyG1un
oRV/oWT3LdqzxvlAqJ9tUfcs2uB2DTkCPX8orFmMrJQqshBsniQ9SA9mJErnAf9o
Z1rpkKyENFkMRwWT2Ok5EexslTLBDahi3LQi08ZLddNX3hmjJHQVWL7eIU2BbXIh
ScgNhXPwts/x1U0N9zdXmQKBgQC4O6W2cAQQNd5XEvUpQ/XrtAmxjjq0xjbxckve
OQFy0/0m9NiuE9bVaniDXgvHm2eKCVZlO8+pw4oZlnE3+an8brCParvrJ0ZCsY1u
H8qgxEEPYdRxsKBe1jBKj0U23JNmQBw+SOqh9AAfbDA2yTzjd7HU4AqXI7SZ3QW/
NHO33wKBgQCqxUmocyqKy5NEBPMmeHWapuSY47bdDaE139vRWV6M47oxzxF8QnQV
1TGWsshK04QO8wsfzIa9/SjZkU17QVkz7LXbq4hPmiZjhP/H+roCeoDEyHFdkq6B
bm/edpYemlJlQhEYtecwvD57NZbVuaqX4Culz9WdSsw4I56hD+QjHQ==
-----END RSA PRIVATE KEY-----
"