Commit 95f1e857 authored by Mathieu Parent's avatar Mathieu Parent

Add service to sign Debian distribution

Also remove unused current_user in GenerateDistributionKeyService.

Changelog: added
parent d0b20742
......@@ -5,20 +5,42 @@ module Packages
class GenerateDistributionKeyService
include Gitlab::Utils::StrongMemoize
def initialize(current_user:, params: {})
@current_user = current_user
def initialize(params: {})
@params = params
end
def execute
raise ArgumentError, 'Please provide a user' unless current_user.is_a?(User)
using_pinentry do |ctx|
# Generate key
ctx.generate_key generate_key_params
key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
fingerprint = key.fingerprint
# Export private key
data = GPGME::Data.new
ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
data.seek 0
private_key = data.read
generate_key
# Export public key
data = GPGME::Data.new
ctx.export_keys fingerprint, data
data.seek 0
public_key = data.read
{
private_key: private_key,
public_key: public_key,
passphrase: passphrase,
fingerprint: fingerprint
}
end
end
private
attr_reader :current_user, :params
attr_reader :params
def passphrase
strong_memoize(:passphrase) do
......@@ -72,35 +94,6 @@ module Packages
}.map { |k, v| "#{k}: #{v}\n" }.join +
'</GnupgKeyParms>'
end
def generate_key
using_pinentry do |ctx|
# Generate key
ctx.generate_key generate_key_params
key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
fingerprint = key.fingerprint
# Export private key
data = GPGME::Data.new
ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
data.seek 0
private_key = data.read
# Export public key
data = GPGME::Data.new
ctx.export_keys fingerprint, data
data.seek 0
public_key = data.read
{
private_key: private_key,
public_key: public_key,
passphrase: passphrase,
fingerprint: fingerprint
}
end
end
end
end
end
# frozen_string_literal: true
module Packages
module Debian
class SignDistributionService
include Gitlab::Utils::StrongMemoize
def initialize(distribution, content, params: {})
@distribution = distribution
@content = content
@params = params
end
def execute
raise ArgumentError, 'distribution key is missing' unless @distribution.key
sig_mode = GPGME::GPGME_SIG_MODE_CLEAR
sig_mode = GPGME::GPGME_SIG_MODE_DETACH if @params[:detach]
Gitlab::Gpg.using_tmp_keychain do
GPGME::Ctx.new(
armor: true,
offline: true,
pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK,
password: @distribution.key.passphrase
) do |ctx|
ctx.import(GPGME::Data.from_str(@distribution.key.public_key))
ctx.import(GPGME::Data.from_str(@distribution.key.private_key))
signature = GPGME::Data.new
ctx.sign(GPGME::Data.from_str(@content), signature, sig_mode)
signature.to_s
end
end
end
end
end
end
......@@ -4,9 +4,9 @@ FactoryBot.define do
factory :debian_project_distribution_key, class: 'Packages::Debian::ProjectDistributionKey' do
distribution { association(:debian_project_distribution) }
private_key { '-----BEGIN PGP PRIVATE KEY BLOCK-----' }
private_key { File.read(Rails.root.join('spec/fixtures/', 'private_key.asc')) }
passphrase { '12345' }
public_key { '-----BEGIN PGP PUBLIC KEY BLOCK-----' }
public_key { File.read(Rails.root.join('spec/fixtures/', 'public_key.asc')) }
fingerprint { '12345' }
factory :debian_group_distribution_key, class: 'Packages::Debian::GroupDistributionKey' do
......
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: Alice's OpenPGP Transferable Secret Key
Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html
lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj
ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ
CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l
nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf
a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB
BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA
/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF
u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM
hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb
Pnn+We1aTBhaGa86AQ==
=n8OM
-----END PGP PRIVATE KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Alice's OpenPGP certificate
Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html
mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
=iIGO
-----END PGP PUBLIC KEY BLOCK-----
......@@ -3,15 +3,12 @@
require 'spec_helper'
RSpec.describe Packages::Debian::GenerateDistributionKeyService do
let_it_be(:user) { create(:user) }
let(:params) { {} }
subject { described_class.new(current_user: user, params: params) }
subject { described_class.new(params: params) }
let(:response) { subject.execute }
context 'with a user' do
it 'returns an Hash', :aggregate_failures do
expect(GPGME::Ctx).to receive(:new).with(armor: true, offline: true).and_call_original
expect(User).to receive(:random_password).with(no_args).and_call_original
......@@ -23,13 +20,4 @@ RSpec.describe Packages::Debian::GenerateDistributionKeyService do
expect(response[:fingerprint].length).to eq(40)
expect(response[:passphrase].length).to be > 10
end
end
context 'without a user' do
let(:user) { nil }
it 'raises an ArgumentError' do
expect { response }.to raise_error(ArgumentError, 'Please provide a user')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Debian::SignDistributionService do
let_it_be(:group) { create(:group, :public) }
let(:content) { FFaker::Lorem.paragraph }
let(:params) { {} }
let(:service) { described_class.new(distribution, content, params: params) }
shared_examples 'Sign Distribution' do |container_type, detach: false|
context "for #{container_type} detach=#{detach}" do
let(:params) { { detach: detach } }
if container_type == :group
let_it_be(:distribution) { create('debian_group_distribution', container: group) }
else
let_it_be(:project) { create(:project, group: group) }
let_it_be(:distribution) { create('debian_project_distribution', container: project) }
end
describe '#execute' do
subject { service.execute }
context 'without an existing key' do
it 'raises ArgumentError', :aggregate_failures do
expect { subject }
.to raise_error(ArgumentError, 'distribution key is missing')
end
end
context 'with an existing key' do
let!(:key) { create("debian_#{container_type}_distribution_key", distribution: distribution)}
it 'returns the content signed', :aggregate_failures do
expect(Packages::Debian::GenerateDistributionKeyService).not_to receive(:new)
key_class = "Packages::Debian::#{container_type.capitalize}DistributionKey".constantize
expect { subject }
.to not_change { key_class.count }
if detach
expect(subject).to start_with("-----BEGIN PGP SIGNATURE-----\n")
else
expect(subject).to start_with("-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n#{content}\n-----BEGIN PGP SIGNATURE-----\n")
end
expect(subject).to end_with("\n-----END PGP SIGNATURE-----\n")
end
end
end
end
end
it_behaves_like 'Sign Distribution', :project
it_behaves_like 'Sign Distribution', :project, detach: true
it_behaves_like 'Sign Distribution', :group
it_behaves_like 'Sign Distribution', :group, detach: true
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