Skip to content

Commit c72ab28

Browse files
committed
feat(certificates): load webhook certificates from database
1 parent 7743ecc commit c72ab28

File tree

10 files changed

+216
-5
lines changed

10 files changed

+216
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require_relative 'migration_helper'
2+
3+
Sequel.migration do
4+
change do
5+
create_table(:certificates, charset: 'utf8') do
6+
primary_key :id
7+
String :uuid, null: false, unique: true, unique_constraint_name: 'uq_certificate_uuid'
8+
String :description, null: true
9+
String :content, null: false, type: PactBroker::MigrationHelper.large_text_type
10+
DateTime :created_at, null: false
11+
DateTime :updated_at, null: false
12+
end
13+
end
14+
end

db/pact_broker_database.sqlite3

36 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module PactBroker
2+
module Certificates
3+
class Certificate < Sequel::Model
4+
end
5+
6+
Certificate.plugin :timestamps, update_on_create: true
7+
end
8+
end
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
require 'pact_broker/certificates/certificate'
2+
require 'pact_broker/logging'
3+
require 'openssl'
4+
5+
module PactBroker
6+
module Certificates
7+
module Service
8+
9+
extend self
10+
extend PactBroker::Logging
11+
12+
def cert_store
13+
cert_store = OpenSSL::X509::Store.new
14+
cert_store.set_default_paths
15+
find_all_certificates.each do | certificate |
16+
begin
17+
cert_store.add_cert(certificate)
18+
rescue StandardError => e
19+
log_error e, "Error adding certificate object #{certificate.to_s} to store"
20+
end
21+
end
22+
cert_store
23+
end
24+
25+
def find_all_certificates
26+
Certificate.collect do | certificate |
27+
cert_arr = certificate.content.split(/(-----END [^\-]+-----)/).each_slice(2).map(&:join)
28+
cert_arr.collect do |c|
29+
begin
30+
OpenSSL::X509::Certificate.new(c)
31+
rescue StandardError => e
32+
log_error e, "Error creating certificate object from certificate #{certificate.uuid} '#{certificate.description}'"
33+
nil
34+
end
35+
end
36+
end.flatten.compact
37+
end
38+
end
39+
end
40+
end

lib/pact_broker/domain/webhook_request.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require 'net/http'
66
require 'pact_broker/webhooks/redact_logs'
77
require 'pact_broker/api/pact_broker_urls'
8+
require 'pact_broker/services'
89

910
module PactBroker
1011

@@ -23,6 +24,7 @@ class WebhookRequest
2324

2425
include PactBroker::Logging
2526
include PactBroker::Messages
27+
include PactBroker::Services
2628

2729
attr_accessor :method, :url, :headers, :body, :username, :password, :uuid
2830

@@ -103,7 +105,7 @@ def build_request uri, pact, execution_logger
103105
def do_request uri, req
104106
logger.info "Making webhook #{uuid} request #{to_s}"
105107
Net::HTTP.start(uri.hostname, uri.port,
106-
:use_ssl => uri.scheme == 'https') do |http|
108+
:use_ssl => uri.scheme == 'https', cert_store: cert_store) do |http|
107109
http.request req
108110
end
109111
end
@@ -153,6 +155,10 @@ def gsub_url pact, url
153155
escaped_pact_url = CGI::escape(pact_url)
154156
url.gsub('${pactbroker.pactUrl}', escaped_pact_url)
155157
end
158+
159+
def cert_store
160+
certificate_service.cert_store
161+
end
156162
end
157163
end
158164
end

lib/pact_broker/services.rb

+5
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,10 @@ def matrix_service
5656
require 'pact_broker/matrix/service'
5757
Matrix::Service
5858
end
59+
60+
def certificate_service
61+
require 'pact_broker/certificates/service'
62+
Certificates::Service
63+
end
5964
end
6065
end

spec/fixtures/certificate-invalid.pem

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
-----BEGIN CERTIFICATE-----
2+
foo
3+
-----END CERTIFICATE-----
4+
-----BEGIN CERTIFICATE-----
5+
MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
6+
RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
7+
bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
8+
IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
9+
ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy
10+
MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
11+
LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
12+
YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
13+
A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
14+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
15+
K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
16+
sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
17+
MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
18+
XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
19+
HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
20+
4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA
21+
vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G
22+
CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA
23+
WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
24+
oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ
25+
h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18
26+
f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN
27+
B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy
28+
vUxFnmG6v4SBkgPR0ml8xQ==
29+
-----END CERTIFICATE-----

spec/fixtures/certificate.pem

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEhjCCA26gAwIBAgIJAOoR4cViLrYBMA0GCSqGSIb3DQEBBQUAMIGIMQswCQYD
3+
VQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExEjAQBgNVBAcTCU1lbGJvdXJuZTEY
4+
MBYGA1UEChMPUGFjdCBGb3VuZGF0aW9uMRUwEwYDVQQDEwxCZXRoIFNrdXJyaWUx
5+
ITAfBgkqhkiG9w0BCQEWEmJldGhAYmV0aGVzcXVlLmNvbTAeFw0xNzEyMDcwMDI1
6+
MjZaFw00NTA0MjQwMDI1MjZaMIGIMQswCQYDVQQGEwJBVTERMA8GA1UECBMIVmlj
7+
dG9yaWExEjAQBgNVBAcTCU1lbGJvdXJuZTEYMBYGA1UEChMPUGFjdCBGb3VuZGF0
8+
aW9uMRUwEwYDVQQDEwxCZXRoIFNrdXJyaWUxITAfBgkqhkiG9w0BCQEWEmJldGhA
9+
YmV0aGVzcXVlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKli
10+
EwzIbbZdoDbuJPkMsdGihSYK6KQrLCsvFmNLEgDBKds387E5mQoOjDvlDHLkY+uq
11+
Lm10bI2MSnQAj9B+jqf48FNaoHq2A30iohmK2hPKsrIMi3eaWrXYm9+ZxnNUO/j6
12+
/WKCsX/InUsZDd0J6F6HO9RZ+/AmfBC6fTuxKFgGENwsZSPIpi2JwBeEV+YCthPf
13+
yOBzgNyljd+BqvZCJK3+r074131TC4AEzWZsV33ipOTpcY3y8XAOa79npWUfnTOX
14+
E3NW1Jqrd9Ozky9+HldGGr0VhRkAUneyhhT0HZsSoYcI6QRaYbA1+GkHNIWtPhhk
15+
coSjhKBXJ2RduJi7eisCAwEAAaOB8DCB7TAdBgNVHQ4EFgQUb7yD7klC/K/sJCoK
16+
SJjhWRvygsYwgb0GA1UdIwSBtTCBsoAUb7yD7klC/K/sJCoKSJjhWRvygsahgY6k
17+
gYswgYgxCzAJBgNVBAYTAkFVMREwDwYDVQQIEwhWaWN0b3JpYTESMBAGA1UEBxMJ
18+
TWVsYm91cm5lMRgwFgYDVQQKEw9QYWN0IEZvdW5kYXRpb24xFTATBgNVBAMTDEJl
19+
dGggU2t1cnJpZTEhMB8GCSqGSIb3DQEJARYSYmV0aEBiZXRoZXNxdWUuY29tggkA
20+
6hHhxWIutgEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEARThJZnP0
21+
21PjLxb0/RZ/W/Rl7qqp5jh6sxPTfdztDpfJHcQztmmXQhZaKmcPtqSp7zFKZKIC
22+
D5b0n1lSKqf1bwxwcGQfBu1EAZMYBSNNRJsdMqqnaj76RFysOye4KXXMrCNJ0Md6
23+
MEObX87XBWiKmy5ZbtfoqiWcHrsw9Bnl+9ZPwIaxndtlaaJbnAyKTP28Z8rltiva
24+
kUxcmh37DpAjEBPMWgiD+pfVWhz54rNN7IqndIJhAE8Zphvq2RguMo6CgMbmwlcv
25+
gf9w/hcz5FES3GA01bDmp9CVdGxWN+njcstuOOrVuFgdFR8z3WokPI4YsACXxIZc
26+
5R2NaXTfO9Mw2w==
27+
-----END CERTIFICATE-----
28+
-----BEGIN CERTIFICATE-----
29+
MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
30+
RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
31+
bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
32+
IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
33+
ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy
34+
MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
35+
LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
36+
YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
37+
A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
38+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
39+
K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
40+
sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
41+
MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
42+
XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
43+
HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
44+
4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA
45+
vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G
46+
CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA
47+
WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
48+
oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ
49+
h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18
50+
f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN
51+
B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy
52+
vUxFnmG6v4SBkgPR0ml8xQ==
53+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'pact_broker/certificates/service'
2+
3+
module PactBroker
4+
module Certificates
5+
describe Service do
6+
let(:certificate_content) { File.read('spec/fixtures/certificate.pem') }
7+
8+
describe "#cert_store" do
9+
subject { Service.cert_store }
10+
11+
it "returns an OpenSSL::X509::Store" do
12+
expect(subject).to be_instance_of(OpenSSL::X509::Store)
13+
end
14+
15+
context "when there is a duplicate certificate" do
16+
before do
17+
Certificate.create(uuid: '1234', content: certificate_content)
18+
Certificate.create(uuid: '5678', content: certificate_content)
19+
end
20+
21+
it "logs the error" do
22+
expect(PactBroker.logger).to receive(:error).with(/Error adding certificate/).at_least(1).times
23+
subject
24+
end
25+
26+
it "returns an OpenSSL::X509::Store" do
27+
expect(subject).to be_instance_of(OpenSSL::X509::Store)
28+
end
29+
end
30+
end
31+
32+
describe "#find_all_certificates" do
33+
let!(:certificate) do
34+
Certificate.create(uuid: '1234', content: certificate_content)
35+
end
36+
37+
subject { Service.find_all_certificates }
38+
39+
context "with a valid certificate file" do
40+
it "returns all the X509 Certificate objects" do
41+
expect(subject.size).to eq 2
42+
end
43+
end
44+
45+
context "with an invalid certificate file" do
46+
let(:certificate_content) { File.read('spec/fixtures/certificate-invalid.pem') }
47+
48+
it "logs an error" do
49+
expect(PactBroker.logger).to receive(:error).with(/Error.*1234/)
50+
subject
51+
end
52+
53+
it "returns all the valid X509 Certificate objects" do
54+
expect(subject.size).to eq 1
55+
end
56+
end
57+
end
58+
end
59+
end
60+
end

spec/lib/pact_broker/domain/webhook_request_spec.rb

-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
require 'webmock/rspec'
44

55
module PactBroker
6-
76
module Domain
8-
97
describe WebhookRequest do
108
before do
119
allow(PactBroker::Api::PactBrokerUrls).to receive(:pact_url).and_return('http://example.org/pact-url')
@@ -33,7 +31,6 @@ module Domain
3331

3432
let(:logs) { subject.execute(pact, options).logs }
3533

36-
3734
describe "description" do
3835
it "returns a brief description of the HTTP request" do
3936
expect(subject.description).to eq 'POST example.org'
@@ -55,7 +52,6 @@ module Domain
5552
end
5653

5754
describe "execute" do
58-
5955
let!(:http_request) do
6056
stub_request(:post, "http://example.org/hook").
6157
with(:headers => {'Content-Type'=>'text/plain'}, :body => 'body').

0 commit comments

Comments
 (0)