certmeister 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +20 -0
- data/README.md +32 -0
- data/Rakefile +6 -0
- data/certmeister.gemspec +26 -0
- data/fixtures/ca.crt +15 -0
- data/fixtures/ca.csr +12 -0
- data/fixtures/ca.key +15 -0
- data/fixtures/client.crt +15 -0
- data/fixtures/client.csr +12 -0
- data/fixtures/client.key +15 -0
- data/lib/certmeister.rb +14 -0
- data/lib/certmeister/base.rb +92 -0
- data/lib/certmeister/config.rb +129 -0
- data/lib/certmeister/in_memory_store.rb +43 -0
- data/lib/certmeister/policy.rb +21 -0
- data/lib/certmeister/policy/blackhole.rb +15 -0
- data/lib/certmeister/policy/chain_all.rb +36 -0
- data/lib/certmeister/policy/domain.rb +37 -0
- data/lib/certmeister/policy/existing.rb +32 -0
- data/lib/certmeister/policy/fcrdns.rb +40 -0
- data/lib/certmeister/policy/noop.rb +15 -0
- data/lib/certmeister/policy/psk.rb +37 -0
- data/lib/certmeister/policy/response.rb +24 -0
- data/lib/certmeister/response.rb +47 -0
- data/lib/certmeister/store_error.rb +6 -0
- data/lib/certmeister/test/memory_store_interface.rb +54 -0
- data/lib/certmeister/version.rb +5 -0
- data/signit.rb +39 -0
- data/spec/certmeister/base_spec.rb +205 -0
- data/spec/certmeister/config_spec.rb +170 -0
- data/spec/certmeister/in_memory_store_spec.rb +40 -0
- data/spec/certmeister/policy/blackhole_spec.rb +19 -0
- data/spec/certmeister/policy/chain_all_spec.rb +40 -0
- data/spec/certmeister/policy/domain_spec.rb +38 -0
- data/spec/certmeister/policy/existing_spec.rb +39 -0
- data/spec/certmeister/policy/fcrdns_spec.rb +45 -0
- data/spec/certmeister/policy/noop_spec.rb +17 -0
- data/spec/certmeister/policy/psk_spec.rb +38 -0
- data/spec/certmeister/policy/response_spec.rb +35 -0
- data/spec/certmeister/response_spec.rb +73 -0
- data/spec/helpers/certmeister_config_helper.rb +21 -0
- data/spec/helpers/certmeister_fetching_request_helper.rb +9 -0
- data/spec/helpers/certmeister_policy_helper.rb +14 -0
- data/spec/helpers/certmeister_removing_request_helper.rb +9 -0
- data/spec/helpers/certmeister_signing_request_helper.rb +10 -0
- data/spec/spec_helper.rb +20 -0
- metadata +159 -0
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/certmeister_config_helper'
|
3
|
+
require 'helpers/certmeister_signing_request_helper'
|
4
|
+
|
5
|
+
require 'certmeister'
|
6
|
+
require 'openssl'
|
7
|
+
|
8
|
+
describe Certmeister do
|
9
|
+
|
10
|
+
it "is configured at instantiation" do
|
11
|
+
expect { Certmeister.new(CertmeisterConfigHelper::valid_config) }.to_not raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it "cannot be instantiated with an invalid config" do
|
15
|
+
expect { Certmeister.new(Certmeister::Config.new({})) }.to raise_error(RuntimeError, /invalid config/)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#sign(request)" do
|
19
|
+
|
20
|
+
let(:valid_request) { CertmeisterSigningRequestHelper::valid_request }
|
21
|
+
|
22
|
+
describe "refuses" do
|
23
|
+
|
24
|
+
it "refuses the request if it has no cn" do
|
25
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
26
|
+
response = ca.sign({})
|
27
|
+
invalid_request = valid_request.tap { |o| o.delete(:cn) }
|
28
|
+
response = ca.sign(invalid_request)
|
29
|
+
expect(response.error).to match /CN/
|
30
|
+
end
|
31
|
+
|
32
|
+
it "refuses the request if the sign policy declines it" do
|
33
|
+
options = CertmeisterConfigHelper::valid_config_options
|
34
|
+
options[:sign_policy] = Certmeister::Policy::Blackhole.new
|
35
|
+
ca = Certmeister.new(Certmeister::Config.new(options))
|
36
|
+
response = ca.sign(valid_request)
|
37
|
+
expect(response.error).to eql "request refused (blackholed)"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "refuses to sign an invalid CSR" do
|
41
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
42
|
+
invalid_request = valid_request.tap { |o| o[:csr] = "a terrible misunderstanding" }
|
43
|
+
response = ca.sign(invalid_request)
|
44
|
+
expect(response.error).to eql "invalid CSR (not enough data)"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "refuses to sign a CSR if the subject does not agree with the request CN" do
|
48
|
+
request = valid_request.tap { |r| r[:cn] = "monkeyface.example.com" }
|
49
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
50
|
+
response = ca.sign(request)
|
51
|
+
expect(response.error).to eql "CSR subject (axl.hetzner.africa) disagrees with request CN (monkeyface.example.com)"
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "signing" do
|
57
|
+
|
58
|
+
def sign_valid_request
|
59
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
60
|
+
ca.sign(valid_request)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "signs a CSR if the sign policy passes the request" do
|
64
|
+
response = sign_valid_request
|
65
|
+
expect(response).to be_hit
|
66
|
+
end
|
67
|
+
|
68
|
+
it "sets the issuer to the subject of the CA certificate" do
|
69
|
+
response = sign_valid_request
|
70
|
+
cert = OpenSSL::X509::Certificate.new(response.pem)
|
71
|
+
expect(cert.issuer.to_s).to match /CN=Certmeister Test CA/
|
72
|
+
end
|
73
|
+
|
74
|
+
it "sets the subject to the subject of the CSR" do
|
75
|
+
response = sign_valid_request
|
76
|
+
cert = OpenSSL::X509::Certificate.new(response.pem)
|
77
|
+
expect(cert.subject.to_s).to match /CN=axl.hetzner.africa/
|
78
|
+
end
|
79
|
+
|
80
|
+
it "sets validity to 5 years from now" do
|
81
|
+
now = (DateTime.now.to_time - 1)
|
82
|
+
response = sign_valid_request
|
83
|
+
cert = OpenSSL::X509::Certificate.new(response.pem)
|
84
|
+
expect(cert.not_before).to be >= now
|
85
|
+
expect(cert.not_after - cert.not_before).to be < (5 * 365 * 24 * 60 * 60 + 2)
|
86
|
+
expect(cert.not_after - cert.not_before).to be >= (5 * 365 * 24 * 60 * 60)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "stores the signed certificate, indexed on request CN" do
|
90
|
+
config = CertmeisterConfigHelper::valid_config
|
91
|
+
ca = Certmeister.new(config)
|
92
|
+
response = ca.sign(valid_request)
|
93
|
+
stored = config.store.fetch('axl.hetzner.africa')
|
94
|
+
expect(stored).to eql response.pem
|
95
|
+
end
|
96
|
+
|
97
|
+
it "does not capture errors from the store" do
|
98
|
+
config = CertmeisterConfigHelper::valid_config
|
99
|
+
config.store.send(:break!)
|
100
|
+
ca = Certmeister.new(config)
|
101
|
+
expect { ca.sign(valid_request) }.to raise_error(Certmeister::StoreError)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#fetch(request)" do
|
109
|
+
|
110
|
+
describe "refuses" do
|
111
|
+
|
112
|
+
it "refuses the request if it has no cn" do
|
113
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
114
|
+
response = ca.fetch({})
|
115
|
+
expect(response.error).to match /CN/
|
116
|
+
end
|
117
|
+
|
118
|
+
it "refuses the request if the fetch policy declines it" do
|
119
|
+
options = CertmeisterConfigHelper::valid_config_options
|
120
|
+
options[:fetch_policy] = Certmeister::Policy::Blackhole.new
|
121
|
+
ca = Certmeister.new(Certmeister::Config.new(options))
|
122
|
+
response = ca.fetch(cn: 'axl.starjuice.net')
|
123
|
+
expect(response.error).to eql "request refused (blackholed)"
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns a miss if the store has no certificate for the cn" do
|
129
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
130
|
+
expect(ca.fetch(cn: 'axl.starjuice.net')).to be_miss
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns the certificate as a PEM-encoded string when the store has a certificate for the cn" do
|
134
|
+
config = CertmeisterConfigHelper::valid_config
|
135
|
+
config.store.store('axl.starjuice.net', '...')
|
136
|
+
ca = Certmeister.new(config)
|
137
|
+
expect(ca.fetch(cn: 'axl.starjuice.net').pem).to eql '...'
|
138
|
+
end
|
139
|
+
|
140
|
+
class StoreWithBrokenFetch
|
141
|
+
def store(cn, cert); end
|
142
|
+
def fetch(cn); raise Certmeister::StoreError.new("simulated error"); end
|
143
|
+
def health_check; end
|
144
|
+
end
|
145
|
+
|
146
|
+
it "does not capture errors from the store" do
|
147
|
+
config = CertmeisterConfigHelper::valid_config
|
148
|
+
config.store.send(:break!)
|
149
|
+
ca = Certmeister.new(config)
|
150
|
+
expect { ca.fetch(cn: 'axl.starjuice.net') }.to raise_error(Certmeister::StoreError)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#remove(cn)" do
|
156
|
+
|
157
|
+
describe "refuses" do
|
158
|
+
|
159
|
+
it "refuses the request if it has no cn" do
|
160
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
161
|
+
response = ca.remove({})
|
162
|
+
expect(response.error).to match /CN/
|
163
|
+
end
|
164
|
+
|
165
|
+
it "refuses the request if the fetch policy declines it" do
|
166
|
+
options = CertmeisterConfigHelper::valid_config_options
|
167
|
+
options[:remove_policy] = Certmeister::Policy::Blackhole.new
|
168
|
+
ca = Certmeister.new(Certmeister::Config.new(options))
|
169
|
+
response = ca.remove(cn: 'axl.starjuice.net')
|
170
|
+
expect(response.error).to eql "request refused (blackholed)"
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
it "returns a hit if the certificate existed in the store" do
|
176
|
+
config = CertmeisterConfigHelper::valid_config
|
177
|
+
config.store.store('axl.starjuice.net', '...')
|
178
|
+
ca = Certmeister.new(config)
|
179
|
+
expect(ca.remove(cn: 'axl.starjuice.net')).to be_hit
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns a miss if the certificate did not exist in the store" do
|
183
|
+
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
184
|
+
expect(ca.remove(cn: 'axl.starjuice.net')).to be_miss
|
185
|
+
end
|
186
|
+
|
187
|
+
it "removes the certificate from the store" do
|
188
|
+
config = CertmeisterConfigHelper::valid_config
|
189
|
+
config.store.store('axl.starjuice.net', '...')
|
190
|
+
ca = Certmeister.new(config)
|
191
|
+
ca.remove(cn: 'axl.starjuice.net')
|
192
|
+
expect(config.store.fetch('axl.starjuice.net')).to be_nil
|
193
|
+
end
|
194
|
+
|
195
|
+
it "does not capture errors from the store" do
|
196
|
+
config = CertmeisterConfigHelper::valid_config
|
197
|
+
config.store.send(:break!)
|
198
|
+
ca = Certmeister.new(config)
|
199
|
+
expect { ca.remove(cn: 'axl.starjuice.net') }.to raise_error(Certmeister::StoreError)
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/certmeister_config_helper'
|
3
|
+
require 'helpers/certmeister_policy_helper'
|
4
|
+
|
5
|
+
require 'certmeister'
|
6
|
+
|
7
|
+
describe Certmeister::Config do
|
8
|
+
|
9
|
+
let(:options) { CertmeisterConfigHelper::valid_config_options }
|
10
|
+
|
11
|
+
def config_option_is_required(option)
|
12
|
+
options.delete(option)
|
13
|
+
config = Certmeister::Config.new(options)
|
14
|
+
expect(config).to_not be_valid
|
15
|
+
expect(config.errors[option]).to eql "is required"
|
16
|
+
end
|
17
|
+
|
18
|
+
def config_option_provides_method_with_arity(option, method, arity)
|
19
|
+
arity_name = case arity
|
20
|
+
when 0 then "nullary"
|
21
|
+
when 1 then "unary"
|
22
|
+
when 2 then "binary"
|
23
|
+
when 3 then "ternary"
|
24
|
+
else
|
25
|
+
raise "broken test helper does not support arity #{4}"
|
26
|
+
end
|
27
|
+
options[option].send(:define_singleton_method, method) { |wrong, number, of, arguments| }
|
28
|
+
|
29
|
+
config = Certmeister::Config.new(options)
|
30
|
+
expect(config).to_not be_valid
|
31
|
+
expect(config.errors[option]).to eql "must provide a #{arity_name} #{method} method"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "does not allow unknown options" do
|
35
|
+
options[:unknown] = 1
|
36
|
+
config = Certmeister::Config.new(options)
|
37
|
+
expect(config).to_not be_valid
|
38
|
+
expect(config.errors[:unknown]).to eql "is not a supported option"
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ":ca_cert" do
|
42
|
+
|
43
|
+
it "is required" do
|
44
|
+
config_option_is_required(:ca_cert)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "must be a PEM-encoded x509 certificate" do
|
48
|
+
options[:ca_cert] = "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n"
|
49
|
+
config = Certmeister::Config.new(options)
|
50
|
+
expect(config).to_not be_valid
|
51
|
+
expect(config.errors[:ca_cert]).to eql "must be a PEM-encoded x509 certificate (nested asn1 error)"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "is accessible as an OpenSSL::X509::Certificate object" do
|
55
|
+
config = Certmeister::Config.new(options)
|
56
|
+
expect(config.ca_cert).to be_a(OpenSSL::X509::Certificate)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ":ca_key" do
|
62
|
+
|
63
|
+
it "is required" do
|
64
|
+
config_option_is_required(:ca_key)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "must be a string containing an x509 certificate in PEM encoding" do
|
68
|
+
options[:ca_key] = "-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----\n"
|
69
|
+
config = Certmeister::Config.new(options)
|
70
|
+
expect(config).to_not be_valid
|
71
|
+
expect(config.errors[:ca_key]).to eql "must be a PEM-encoded private key (Could not parse PKey)"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "is accessible as an OpenSSL::PKey::PKey object" do
|
75
|
+
config = Certmeister::Config.new(options)
|
76
|
+
expect(config.ca_key).to be_a(OpenSSL::PKey::PKey)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ":store" do
|
82
|
+
|
83
|
+
it "is required" do
|
84
|
+
config_option_is_required(:store)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "must provide a binary store method" do
|
88
|
+
config_option_provides_method_with_arity(:store, :store, 2)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "must provide a unary fetch method" do
|
92
|
+
config_option_provides_method_with_arity(:store, :fetch, 1)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "must provide a unary remove method" do
|
96
|
+
config_option_provides_method_with_arity(:store, :remove, 1)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "must provide a nullary health_check method" do
|
100
|
+
config_option_provides_method_with_arity(:store, :health_check, 0)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "is accessible" do
|
104
|
+
config = Certmeister::Config.new(options)
|
105
|
+
expect(config.store).to eql options[:store]
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
[:sign_policy, :fetch_policy, :remove_policy].each do |policy|
|
111
|
+
describe ":#{policy}" do
|
112
|
+
|
113
|
+
it "is required" do
|
114
|
+
config_option_is_required(policy)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "must provide a unary authenticate method" do
|
118
|
+
config_option_provides_method_with_arity(policy, :authenticate, 1)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "must return a Certmeister::Policy::Response from the authenticate method" do
|
122
|
+
options[policy] = CertmeisterPolicyHelper::BrokenPolicy.new
|
123
|
+
config = Certmeister::Config.new(options)
|
124
|
+
expect(config).to_not be_valid
|
125
|
+
expect(config.errors[policy]).to eql "must return a policy response"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "is accessible" do
|
129
|
+
config = Certmeister::Config.new(options)
|
130
|
+
expect(config.send(policy)).to eql options[policy]
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "error_list" do
|
137
|
+
|
138
|
+
it "is empty if the config has no errors" do
|
139
|
+
config = Certmeister::Config.new(options)
|
140
|
+
config.valid?
|
141
|
+
expect(config.error_list).to be_empty
|
142
|
+
end
|
143
|
+
|
144
|
+
it "includes one string (option and message) per error if the config has errors" do
|
145
|
+
options.delete(:ca_cert)
|
146
|
+
options.delete(:ca_key)
|
147
|
+
config = Certmeister::Config.new(options)
|
148
|
+
config.valid?
|
149
|
+
expect(config.error_list).to match_array ["ca_cert is required", "ca_key is required"]
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "openssl_digest" do
|
155
|
+
|
156
|
+
it "causes a validation failure if the OpenSSL library doesn't provide one" do
|
157
|
+
expect(OpenSSL::Digest).to receive(:const_defined?).twice.and_return(nil)
|
158
|
+
config = Certmeister::Config.new(options)
|
159
|
+
expect(config).to_not be_valid
|
160
|
+
expect(config.errors[:openssl_digest]).to eql "can't find FIPS 140-2 compliant algorithm in OpenSSL::Digest"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "is accessible without being supplied" do
|
164
|
+
config = Certmeister::Config.new(options)
|
165
|
+
expect([OpenSSL::Digest::SHA256, OpenSSL::Digest::SHA1]).to include(config.openssl_digest)
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'certmeister/test/memory_store_interface'
|
3
|
+
|
4
|
+
require 'certmeister/in_memory_store'
|
5
|
+
|
6
|
+
describe Certmeister::InMemoryStore do
|
7
|
+
|
8
|
+
class << self
|
9
|
+
include Certmeister::Test::MemoryStoreInterface
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like_a_certmeister_store
|
13
|
+
|
14
|
+
describe "for use in testing" do
|
15
|
+
|
16
|
+
it "can be initialized with an existing data set" do
|
17
|
+
existing = {'axl.hetzner.africa' => '...cert...'}
|
18
|
+
store = Certmeister::InMemoryStore.new(existing)
|
19
|
+
expect(store.fetch('axl.hetzner.africa')).to eql '...cert...'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "store raises Certmeister::StoreError when broken" do
|
23
|
+
subject.send(:break!)
|
24
|
+
expect { subject.store('axl.hetzner.africa', "first") }.to raise_error(Certmeister::StoreError)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "fetch raises Certmeister::StoreError when broken" do
|
28
|
+
subject.send(:break!)
|
29
|
+
expect { subject.fetch('axl.hetzner.africa') }.to raise_error(Certmeister::StoreError, "in-memory store is broken")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "remove raises Certmeister::StoreError when broken" do
|
33
|
+
subject.send(:break!)
|
34
|
+
expect { subject.remove('axl.hetzner.africa') }.to raise_error(Certmeister::StoreError, "in-memory store is broken")
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'helpers/certmeister_signing_request_helper'
|
3
|
+
|
4
|
+
require 'certmeister/policy/blackhole'
|
5
|
+
|
6
|
+
describe Certmeister::Policy::Blackhole do
|
7
|
+
|
8
|
+
it "demands a request" do
|
9
|
+
expect { subject.authenticate }.to raise_error(ArgumentError)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "refuses any request" do
|
13
|
+
response = subject.authenticate(CertmeisterSigningRequestHelper::valid_request)
|
14
|
+
expect(response).to_not be_authenticated
|
15
|
+
expect(response.error).to eql "blackholed"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'certmeister/policy/blackhole'
|
3
|
+
require 'certmeister/policy/noop'
|
4
|
+
|
5
|
+
require 'certmeister/policy/chain_all'
|
6
|
+
|
7
|
+
describe Certmeister::Policy::ChainAll do
|
8
|
+
|
9
|
+
it "must be configured with a list of policys" do
|
10
|
+
expected_error = "enumerable collection of policys required"
|
11
|
+
expect { Certmeister::Policy::ChainAll.new }.to raise_error(ArgumentError)
|
12
|
+
expect { Certmeister::Policy::ChainAll.new(Certmeister::Policy::Noop.new) }.to raise_error(ArgumentError, expected_error)
|
13
|
+
expect { Certmeister::Policy::ChainAll.new([]) }.to raise_error(ArgumentError, expected_error)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "demands a request" do
|
17
|
+
policy = Certmeister::Policy::ChainAll.new([Certmeister::Policy::Noop.new])
|
18
|
+
expect { policy.authenticate }.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "authenticates a request that all its chained policys authenticate" do
|
22
|
+
policy = Certmeister::Policy::ChainAll.new([Certmeister::Policy::Noop.new, Certmeister::Policy::Noop.new])
|
23
|
+
response = policy.authenticate({anything: 'something'})
|
24
|
+
expect(response).to be_authenticated
|
25
|
+
end
|
26
|
+
|
27
|
+
it "refuses a request that any one of its chained policys refuses" do
|
28
|
+
refuse_last = Certmeister::Policy::ChainAll.new([ Certmeister::Policy::Noop.new, Certmeister::Policy::Blackhole.new])
|
29
|
+
refuse_first = Certmeister::Policy::ChainAll.new([ Certmeister::Policy::Blackhole.new, Certmeister::Policy::Noop.new])
|
30
|
+
policys = [refuse_last, refuse_first]
|
31
|
+
|
32
|
+
policys.each do |policy|
|
33
|
+
response = policy.authenticate({anything: 'something'})
|
34
|
+
expect(response).to_not be_authenticated
|
35
|
+
expect(response.error).to eql "blackholed"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|