-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into bsfy-181-fix
- Loading branch information
Showing
17 changed files
with
355 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
module Travis::API::V3 | ||
class Models::CustomKey < Model | ||
belongs_to :owner, polymorphic: true | ||
|
||
serialize :private_key, Travis::Model::EncryptedColumn.new | ||
|
||
validates_each :private_key do |record, attr, private_key| | ||
record.errors.add(attr, :missing_attr, message: 'missing_attr') if private_key.blank? | ||
record.errors.add(attr, :invalid_pem, message: 'invalid_pem') unless record.valid_pem? | ||
end | ||
|
||
def save_key!(owner_type, owner_id, name, description, private_key, added_by) | ||
self.owner_type = owner_type | ||
self.owner_id = owner_id | ||
self.private_key = private_key | ||
self.name = name | ||
self.description = description | ||
self.added_by = added_by | ||
|
||
if self.valid? | ||
self.fingerprint = calculate_fingerprint(private_key) | ||
self.public_key = OpenSSL::PKey::RSA.new(private_key).public_key.to_s | ||
|
||
self.save! | ||
end | ||
|
||
self | ||
end | ||
|
||
def valid_pem? | ||
private_key && OpenSSL::PKey::RSA.new(private_key) | ||
true | ||
rescue OpenSSL::PKey::RSAError | ||
false | ||
end | ||
|
||
private | ||
|
||
def calculate_fingerprint(source) | ||
rsa_key = OpenSSL::PKey::RSA.new(source) | ||
public_ssh_rsa = "\x00\x00\x00\x07ssh-rsa" + rsa_key.e.to_s(0) + rsa_key.n.to_s(0) | ||
OpenSSL::Digest::MD5.new(public_ssh_rsa).hexdigest.scan(/../).join(':') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
module Travis::API::V3 | ||
class Queries::CustomKey < Query | ||
def create(params, current_user) | ||
raise UnprocessableEntity, 'Key with this identifier already exists.' unless Travis::API::V3::Models::CustomKey.where(name: params['name'], owner_id: params['owner_id'], owner_type: params['owner_type']).count.zero? | ||
|
||
if params['owner_type'] == 'User' | ||
org_ids = User.find(params['owner_id']).organizations.map(&:id) | ||
|
||
raise UnprocessableEntity, 'Key with this identifier already exists in one of your organizations.' unless Travis::API::V3::Models::CustomKey.where(name: params['name'], owner_id: org_ids, owner_type: 'Organization').count.zero? | ||
elsif params['owner_type'] == 'Organization' | ||
user_ids = Membership.where(organization_id: params['owner_id']).map(&:user_id) | ||
|
||
raise UnprocessableEntity, 'Key with this identifier already exists for your user.' unless Travis::API::V3::Models::CustomKey.where(name: params['name'], owner_id: user_ids, owner_type: 'User').count.zero? | ||
end | ||
|
||
key = Travis::API::V3::Models::CustomKey.new.save_key!( | ||
params['owner_type'], | ||
params['owner_id'], | ||
params['name'], | ||
params['description'], | ||
params['private_key'], | ||
params['added_by'] | ||
) | ||
handle_errors(key) unless key.valid? | ||
|
||
Travis::API::V3::Models::Audit.create!( | ||
owner: current_user, | ||
change_source: 'travis-api', | ||
source: key, | ||
source_changes: { | ||
action: 'create', | ||
fingerprint: key.fingerprint | ||
} | ||
) | ||
|
||
key | ||
end | ||
|
||
def delete(params, current_user) | ||
key = Travis::API::V3::Models::CustomKey.find(params['id']) | ||
Travis::API::V3::Models::Audit.create!( | ||
owner: current_user, | ||
change_source: 'travis-api', | ||
source: key, | ||
source_changes: { | ||
action: 'delete', | ||
name: key.name, | ||
owner_type: key.owner_type, | ||
owner_id: key.owner_id, | ||
fingerprint: key.fingerprint | ||
} | ||
) | ||
|
||
key.destroy | ||
end | ||
|
||
private | ||
|
||
def handle_errors(key) | ||
private_key = key.errors[:private_key] | ||
raise UnprocessableEntity, 'This key is not a private key.' if private_key.include?('invalid_pem') | ||
raise WrongParams if private_key.include?('missing_attr') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module Travis::API::V3 | ||
class Renderer::CustomKey < ModelRenderer | ||
representation :standard, :id, :name, :description, :public_key, :fingerprint, :added_by_login, :created_at | ||
representation :minimal, *representations[:standard] | ||
|
||
def added_by_login | ||
model.added_by.nil? ? '' : User.find(model.added_by).login | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module Travis::API::V3 | ||
class Services::CustomKey::Delete < Service | ||
def run! | ||
raise LoginRequired unless access_control.full_access_or_logged_in? | ||
|
||
query(:custom_key).delete(params, access_control.user) | ||
deleted | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module Travis::API::V3 | ||
class Services::CustomKeys::Create < Service | ||
params :owner_id, :owner_type, :name, :description, :private_key, :added_by | ||
result_type :custom_key | ||
|
||
def run! | ||
raise LoginRequired unless access_control.full_access_or_logged_in? | ||
|
||
result query(:custom_key).create(params, access_control.user) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
describe Travis::API::V3::Models::CustomKey do | ||
let(:user) { FactoryBot.create(:user) } | ||
let(:owner_type) { 'User' } | ||
let(:owner_id) { user.id } | ||
let(:name) { 'TEST_KEY' } | ||
let(:added_by) { user.id } | ||
let(:private_key) { OpenSSL::PKey::RSA.new(TEST_PRIVATE_KEY).to_pem } | ||
|
||
subject { Travis::API::V3::Models::CustomKey.new } | ||
|
||
it 'must save valid private key' do | ||
key = subject.save_key!(owner_type, owner_id, name, '', private_key, added_by) | ||
|
||
expect(key.name).to eq(name) | ||
expect(key.fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') | ||
end | ||
|
||
it 'must not save invalid private key' do | ||
key = subject.save_key!(owner_type, owner_id, name, '', 'INVALID', added_by) | ||
|
||
expect(key.errors.messages[:private_key]).to eq(['invalid_pem']) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
describe Travis::API::V3::Queries::CustomKey do | ||
let(:private_key) { OpenSSL::PKey::RSA.new(TEST_PRIVATE_KEY).to_pem } | ||
|
||
context 'owner type is user' do | ||
let(:user) { FactoryBot.create(:user) } | ||
let(:params) do | ||
{ | ||
'owner_id' => user.id, | ||
'owner_type' => 'User', | ||
'added_by' => user.id, | ||
'name' => 'TEST_KEY', | ||
'private_key' => private_key, | ||
'description' => '' | ||
} | ||
end | ||
|
||
context 'key with identifier does not exist in user or organization' do | ||
it 'creates custom key' do | ||
expect(described_class.new({}, 'CustomKey').create(params).fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') | ||
end | ||
end | ||
|
||
context 'key with identifier exists for this user' do | ||
before do | ||
Travis::API::V3::Models::CustomKey.new.save_key!( | ||
params['owner_type'], | ||
params['owner_id'], | ||
params['name'], | ||
params['description'], | ||
params['private_key'], | ||
params['added_by'] | ||
) | ||
end | ||
|
||
it 'returns error' do | ||
expect { described_class.new({}, 'CustomKey').create(params) }.to raise_error(Travis::API::V3::UnprocessableEntity) | ||
end | ||
end | ||
|
||
context 'key with identifier exists for users organization' do | ||
let(:org) { FactoryBot.create(:org) } | ||
let!(:membership) { org.memberships.create(user: user, role: 'admin', build_permission: true) } | ||
|
||
before do | ||
Travis::API::V3::Models::CustomKey.new.save_key!( | ||
'Organization', | ||
org.id, | ||
params['name'], | ||
params['description'], | ||
params['private_key'], | ||
params['added_by'] | ||
) | ||
end | ||
|
||
it 'returns error' do | ||
expect { described_class.new({}, 'CustomKey').create(params) }.to raise_error(Travis::API::V3::UnprocessableEntity) | ||
end | ||
end | ||
end | ||
|
||
context 'owner type is organization' do | ||
let(:org) { FactoryBot.create(:org) } | ||
let(:user) { FactoryBot.create(:user) } | ||
let!(:membership) { org.memberships.create(user: user, role: 'admin', build_permission: true) } | ||
let(:params) do | ||
{ | ||
'owner_id' => org.id, | ||
'owner_type' => 'Organization', | ||
'added_by' => user.id, | ||
'name' => 'TEST_KEY', | ||
'private_key' => private_key, | ||
'description' => '' | ||
} | ||
end | ||
|
||
context 'key with identifier does not exist in user or organization' do | ||
it 'creates custom key' do | ||
expect(described_class.new({}, 'CustomKey').create(params).fingerprint).to eq('57:78:65:c2:c9:c8:c9:f7:dd:2b:35:39:40:27:d2:40') | ||
end | ||
end | ||
|
||
context 'key with identifier exists for this user' do | ||
before do | ||
Travis::API::V3::Models::CustomKey.new.save_key!( | ||
'User', | ||
user.id, | ||
params['name'], | ||
params['description'], | ||
params['private_key'], | ||
params['added_by'] | ||
) | ||
end | ||
|
||
it 'returns error' do | ||
expect { described_class.new({}, 'CustomKey').create(params) }.to raise_error(Travis::API::V3::UnprocessableEntity) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
describe Travis::API::V3::Services::CustomKey::Delete, set_app: true do | ||
let(:user) { FactoryBot.create(:user) } | ||
let(:private_key) { OpenSSL::PKey::RSA.new(TEST_PRIVATE_KEY).to_pem } | ||
let(:custom_key) { Travis::API::V3::Models::CustomKey.new.save_key!('User', user.id, 'TEST_KEY', '', private_key, user.id) } | ||
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) } | ||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} | ||
let(:parsed_body) { JSON.load(body) } | ||
|
||
describe "deleting a custom key by id" do | ||
before { delete("/v3/custom_key/#{custom_key.id}", {}, headers) } | ||
example { expect(last_response.status).to eq 204 } | ||
example { expect(Travis::API::V3::Models::CustomKey.where(id: custom_key.id)).to be_empty } | ||
example { expect(parsed_body).to be_nil } | ||
end | ||
end |
Oops, something went wrong.