conjur-api 4.28.1 → 4.29.0
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +1 -1
- data/conjur-api.gemspec +2 -1
- data/jenkins.sh +1 -1
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur/base.rb +129 -25
- data/lib/conjur/configuration.rb +8 -0
- data/spec/lib/api_spec.rb +44 -1
- data/spec/lib/configuration_spec.rb +93 -63
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0564b6aa766b949e956c83932c25994999c8e03
|
|
4
|
+
data.tar.gz: 03be39ecd89a7b370115ce168c4b3c54acd45b09
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d04e0fb688351001e486bc01613ecd5e1cd4ec66e7790f0ed802b9a7d4eb5280fef68498cf720ebe4a3cfc5a270e190a2e970fea8c9419cec16dbd77060b69d1
|
|
7
|
+
data.tar.gz: c64f11b333a74bfa8f5af3ad0985daa7b25f82d55db7514f00589f210be408d04079792b7a5ff8efe23922a5967e04e8759457b62c4fd8209b02d6daf7984232
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# v4.29.0
|
|
2
|
+
|
|
3
|
+
* Add `Conjur::API#new_from_token_file` to create an API instance from a file which contains an access token, which should be periodically updated by another process.
|
|
4
|
+
|
|
5
|
+
# v4.28.2
|
|
6
|
+
|
|
7
|
+
* Make sure certificate file is readable before trying to use it.
|
|
8
|
+
|
|
1
9
|
# v4.28.1
|
|
2
10
|
|
|
3
11
|
* `Conjur::API#ldap_sync_policy` now returns log events generated when
|
data/Gemfile
CHANGED
data/conjur-api.gemspec
CHANGED
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
|
23
23
|
gem.add_dependency 'activesupport'
|
|
24
24
|
gem.add_dependency 'semantic'
|
|
25
25
|
|
|
26
|
-
gem.add_development_dependency 'rake'
|
|
26
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
|
27
27
|
gem.add_development_dependency 'spork'
|
|
28
28
|
gem.add_development_dependency 'rspec', '~> 3'
|
|
29
29
|
gem.add_development_dependency 'rspec-expectations', '~> 3.4'
|
|
@@ -40,4 +40,5 @@ Gem::Specification.new do |gem|
|
|
|
40
40
|
gem.add_development_dependency 'redcarpet'
|
|
41
41
|
gem.add_development_dependency 'tins', '~> 1.6', '< 1.7.0'
|
|
42
42
|
gem.add_development_dependency 'inch'
|
|
43
|
+
gem.add_development_dependency 'fakefs'
|
|
43
44
|
end
|
data/jenkins.sh
CHANGED
data/lib/conjur-api/version.rb
CHANGED
data/lib/conjur/base.rb
CHANGED
|
@@ -117,11 +117,6 @@ module Conjur
|
|
|
117
117
|
# 'super-secret' might get the token from a request header, create an {Conjur::API} instance with this method,
|
|
118
118
|
# and use {Conjur::Resource#permitted?} to decide whether to accept and forward the request.
|
|
119
119
|
#
|
|
120
|
-
# Note that Conjur tokens are issued as JSON. This method expects to get the token as a parsed JSON Hash.
|
|
121
|
-
# When sending tokens as headers, you will normally use base64 encoded strings. Authorization headers
|
|
122
|
-
# used by Conjur have the form `'Token token="#{b64encode token.to_json}"'`, but this format is in no way
|
|
123
|
-
# required.
|
|
124
|
-
#
|
|
125
120
|
# @example A simple gatekeeper
|
|
126
121
|
# RESOURCE_NAME = 'protected-service'
|
|
127
122
|
#
|
|
@@ -141,6 +136,21 @@ module Conjur
|
|
|
141
136
|
def new_from_token(token, remote_ip = nil)
|
|
142
137
|
self.new.init_from_token token, remote_ip
|
|
143
138
|
end
|
|
139
|
+
|
|
140
|
+
# Create a new {Conjur::API} instance from a file containing a token issued by the
|
|
141
|
+
# {http://developer.conjur.net/reference/services/authentication Conjur authentication service}.
|
|
142
|
+
# The file is read the first time that a token is required. It is also re-read
|
|
143
|
+
# whenever the API decides that the token it already has is getting close to expiration.
|
|
144
|
+
#
|
|
145
|
+
# This method is useful when an external process, such as a sidecar container, is continuously
|
|
146
|
+
# obtaining fresh tokens and writing them to a known file.
|
|
147
|
+
#
|
|
148
|
+
# @param [String] token_file the file path containing an authentication token as parsed JSON.
|
|
149
|
+
# @param [String] remote_ip the optional IP address to be recorded in the audit record.
|
|
150
|
+
# @return [Conjur::API] an api that will authenticate with the tokens provided in the file.
|
|
151
|
+
def new_from_token_file(token_file, remote_ip = nil)
|
|
152
|
+
self.new.init_from_token_file token_file, remote_ip
|
|
153
|
+
end
|
|
144
154
|
|
|
145
155
|
def encode_audit_ids(ids)
|
|
146
156
|
ids.collect{|id| CGI::escape(id)}.join('&')
|
|
@@ -253,52 +263,146 @@ module Conjur
|
|
|
253
263
|
end
|
|
254
264
|
end
|
|
255
265
|
|
|
266
|
+
module MonotonicTime
|
|
267
|
+
def monotonic_time
|
|
268
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
|
269
|
+
rescue
|
|
270
|
+
# fall back to normal clock if there's no CLOCK_MONOTONIC
|
|
271
|
+
Time.now.to_f
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
module TokenExpiration
|
|
276
|
+
include MonotonicTime
|
|
277
|
+
|
|
278
|
+
# The four minutes is to work around a bug in Conjur < 4.7 causing a 404 on
|
|
279
|
+
# long-running operations (when the token is used right around the 5 minute mark).
|
|
280
|
+
TOKEN_STALE = 4.minutes
|
|
281
|
+
|
|
282
|
+
attr_accessor :token_born
|
|
283
|
+
|
|
284
|
+
def needs_token_refresh?
|
|
285
|
+
token_age > TOKEN_STALE
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def token_age
|
|
289
|
+
gettime - token_born
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# When the API is constructed with an API key, the token can be refreshed using
|
|
294
|
+
# the username and API key. This authenticator assumes that the token was
|
|
295
|
+
# minted immediately before the API instance was created.
|
|
296
|
+
class APIKeyAuthenticator
|
|
297
|
+
include TokenExpiration
|
|
298
|
+
|
|
299
|
+
attr_reader :username, :api_key
|
|
300
|
+
|
|
301
|
+
def initialize username, api_key
|
|
302
|
+
@username = username
|
|
303
|
+
@api_key = api_key
|
|
304
|
+
update_token_born
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def refresh_token
|
|
308
|
+
Conjur::API.authenticate(username, api_key).tap do
|
|
309
|
+
update_token_born
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def update_token_born
|
|
314
|
+
self.token_born = gettime
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def gettime
|
|
318
|
+
monotonic_time
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# When the API is constructed with a token, the token cannot be refreshed.
|
|
323
|
+
class UnableAuthenticator
|
|
324
|
+
include MonotonicTime
|
|
325
|
+
|
|
326
|
+
def refresh_token
|
|
327
|
+
raise "Unable to re-authenticate using an access token"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def needs_token_refresh?
|
|
331
|
+
false
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Obtains fresh tokens by reading them from a file. Some other process is assumed
|
|
336
|
+
# to be acquiring tokens and storing them to the file on a regular basis.
|
|
337
|
+
#
|
|
338
|
+
# This authenticator assumes that the token was created immediately before
|
|
339
|
+
# it was written to the file.
|
|
340
|
+
class TokenFileAuthenticator
|
|
341
|
+
attr_reader :token_file
|
|
342
|
+
|
|
343
|
+
def initialize token_file
|
|
344
|
+
@token_file = token_file
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
attr_reader :last_mtime
|
|
348
|
+
|
|
349
|
+
def mtime
|
|
350
|
+
File.mtime token_file
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def refresh_token
|
|
354
|
+
# There's a race condition here in which the file could be updated
|
|
355
|
+
# after we read the mtime but before we read the file contents. So to be
|
|
356
|
+
# conservative, use the oldest possible mtime.
|
|
357
|
+
mtime = self.mtime
|
|
358
|
+
File.open token_file, 'r' do |f|
|
|
359
|
+
JSON.load(f.read).tap { @last_mtime = mtime }
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def needs_token_refresh?
|
|
364
|
+
mtime != last_mtime
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
256
368
|
def init_from_key username, api_key, remote_ip = nil
|
|
257
369
|
@username = username
|
|
258
370
|
@api_key = api_key
|
|
259
371
|
@remote_ip = remote_ip
|
|
372
|
+
@authenticator = APIKeyAuthenticator.new(username, api_key)
|
|
260
373
|
self
|
|
261
374
|
end
|
|
262
375
|
|
|
263
376
|
def init_from_token token, remote_ip = nil
|
|
264
377
|
@token = token
|
|
265
378
|
@remote_ip = remote_ip
|
|
379
|
+
@authenticator = UnableAuthenticator.new
|
|
380
|
+
self
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def init_from_token_file token_file, remote_ip = nil
|
|
384
|
+
@remote_ip = remote_ip
|
|
385
|
+
@authenticator = TokenFileAuthenticator.new(token_file)
|
|
266
386
|
self
|
|
267
387
|
end
|
|
268
388
|
|
|
389
|
+
attr_reader :authenticator
|
|
390
|
+
|
|
269
391
|
private
|
|
270
|
-
attr_accessor :token_born
|
|
271
392
|
|
|
272
393
|
# Tries to refresh the token if possible.
|
|
273
394
|
#
|
|
274
395
|
# @return [Hash, false] false if the token couldn't be refreshed due to
|
|
275
396
|
# unavailable API key; otherwise, the new token.
|
|
276
397
|
def refresh_token
|
|
277
|
-
|
|
278
|
-
self.token_born = gettime
|
|
279
|
-
@token = Conjur::API.authenticate(@username, @api_key)
|
|
398
|
+
@token = @authenticator.refresh_token
|
|
280
399
|
end
|
|
281
400
|
|
|
282
|
-
# The four minutes is to work around a bug in Conjur < 4.7 causing a 404 on
|
|
283
|
-
# long-running operations (when the token is used right around the 5 minute mark).
|
|
284
|
-
TOKEN_STALE = 4.minutes
|
|
285
|
-
|
|
286
401
|
# Checks if the token is old (or not present).
|
|
287
402
|
#
|
|
288
403
|
# @return [Boolean]
|
|
289
404
|
def needs_token_refresh?
|
|
290
|
-
!@token ||
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def gettime
|
|
294
|
-
Process.clock_gettime Process::CLOCK_MONOTONIC
|
|
295
|
-
rescue
|
|
296
|
-
# fall back to normal clock if there's no CLOCK_MONOTONIC
|
|
297
|
-
Time.now.to_f
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
def token_age
|
|
301
|
-
token_born && (gettime - token_born)
|
|
405
|
+
!@token || @authenticator.needs_token_refresh?
|
|
302
406
|
end
|
|
303
407
|
end
|
|
304
408
|
end
|
data/lib/conjur/configuration.rb
CHANGED
|
@@ -420,6 +420,7 @@ module Conjur
|
|
|
420
420
|
end
|
|
421
421
|
end
|
|
422
422
|
elsif cert_file
|
|
423
|
+
ensure_cert_readable!(cert_file)
|
|
423
424
|
store.add_file cert_file
|
|
424
425
|
else
|
|
425
426
|
return false
|
|
@@ -468,5 +469,12 @@ module Conjur
|
|
|
468
469
|
name.downcase.gsub(/[^a-z0-9\-]/, '-')
|
|
469
470
|
end
|
|
470
471
|
|
|
472
|
+
def ensure_cert_readable!(path)
|
|
473
|
+
# Try to open the file to make sure it exists and that it's
|
|
474
|
+
# readable. Don't rescue exceptions from it, just let them
|
|
475
|
+
# propagate.
|
|
476
|
+
File.open(path) {}
|
|
477
|
+
end
|
|
478
|
+
|
|
471
479
|
end
|
|
472
480
|
end
|
data/spec/lib/api_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
+
require 'fakefs/spec_helpers'
|
|
2
3
|
|
|
3
4
|
shared_examples_for "API endpoint" do
|
|
4
5
|
before { Conjur.configuration = Conjur::Configuration.new }
|
|
@@ -238,13 +239,55 @@ describe Conjur::API do
|
|
|
238
239
|
subject(:api) { Conjur::API.new_from_key(*api_args) }
|
|
239
240
|
end
|
|
240
241
|
|
|
242
|
+
shared_context "logged in with a token file", logged_in: :token_file do
|
|
243
|
+
include FakeFS::SpecHelpers
|
|
244
|
+
include_context "logged in"
|
|
245
|
+
let(:token_file) { "token_file" }
|
|
246
|
+
let(:api_args) { [ token_file, remote_ip ].compact }
|
|
247
|
+
subject(:api) { Conjur::API.new_from_token_file(*api_args) }
|
|
248
|
+
end
|
|
249
|
+
|
|
241
250
|
def time_travel delta
|
|
242
|
-
allow(api).to receive(:gettime).and_wrap_original do |m|
|
|
251
|
+
allow(api.authenticator).to receive(:gettime).and_wrap_original do |m|
|
|
252
|
+
m[] + delta
|
|
253
|
+
end
|
|
254
|
+
allow(api.authenticator).to receive(:monotonic_time).and_wrap_original do |m|
|
|
255
|
+
m[] + delta
|
|
256
|
+
end
|
|
257
|
+
allow(Time).to receive(:now).and_wrap_original do |m|
|
|
243
258
|
m[] + delta
|
|
244
259
|
end
|
|
245
260
|
end
|
|
246
261
|
|
|
247
262
|
describe '#token' do
|
|
263
|
+
context 'with token file available', logged_in: :token_file do
|
|
264
|
+
def write_token token
|
|
265
|
+
File.write token_file, JSON.generate(token)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
before do
|
|
269
|
+
write_token token
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it "reads the file to get a token" do
|
|
273
|
+
expect(api.instance_variable_get("@token")).to eq(nil)
|
|
274
|
+
expect(api.token).to eq(token)
|
|
275
|
+
expect(api.credentials).to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login })
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
context "after expiration" do
|
|
279
|
+
it 'it reads a new token' do
|
|
280
|
+
expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
|
|
281
|
+
|
|
282
|
+
time_travel 6.minutes
|
|
283
|
+
new_token = token.merge "timestamp" => Time.now.to_s
|
|
284
|
+
write_token new_token
|
|
285
|
+
|
|
286
|
+
expect(api.token).to eq(new_token)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
248
291
|
context 'with API key available', logged_in: :api_key do
|
|
249
292
|
it "authenticates to get a token" do
|
|
250
293
|
expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return token
|
|
@@ -215,6 +215,8 @@ describe Conjur::Configuration do
|
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
describe "apply_cert_config!" do
|
|
218
|
+
let (:cert_exists) { true }
|
|
219
|
+
let (:cert_readable) { true }
|
|
218
220
|
subject{ Conjur.configuration.apply_cert_config! }
|
|
219
221
|
|
|
220
222
|
let(:store){ double('default store') }
|
|
@@ -223,42 +225,47 @@ describe Conjur::Configuration do
|
|
|
223
225
|
stub_const 'OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE', store
|
|
224
226
|
allow_any_instance_of(Conjur::Configuration).to receive(:ssl_certificate).and_return ssl_certificate
|
|
225
227
|
allow_any_instance_of(Conjur::Configuration).to receive(:cert_file).and_return cert_file
|
|
228
|
+
allow_any_instance_of(Conjur::Configuration).to receive(:ensure_cert_readable!).with(cert_file) do
|
|
229
|
+
raise Errno::ENOENT unless cert_exists
|
|
230
|
+
raise Errno::EPERM unless cert_readable
|
|
231
|
+
end
|
|
226
232
|
end
|
|
227
233
|
|
|
228
|
-
context
|
|
229
|
-
|
|
230
|
-
|
|
234
|
+
context 'when cert file may exist' do
|
|
235
|
+
context "when neither cert_file or ssl_certificate is present" do
|
|
236
|
+
let(:cert_file){ nil }
|
|
237
|
+
let(:ssl_certificate){ nil }
|
|
231
238
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
it 'does nothing to the store' do
|
|
240
|
+
expect(store).to_not receive(:add_file)
|
|
241
|
+
expect(store).to_not receive(:add_cert)
|
|
242
|
+
expect(subject).to be_falsey
|
|
243
|
+
end
|
|
236
244
|
end
|
|
237
|
-
end
|
|
238
245
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
context 'when both are given' do
|
|
247
|
+
let(:cert_file){ '/path/to/cert.pem' }
|
|
248
|
+
let(:ssl_certificate){ "-----BEGIN CERTIFICATE-----\nfoo\n-----END CERTIFICATE-----\n" }
|
|
249
|
+
let(:cert){ double('certificate') }
|
|
250
|
+
it 'calls store.add_cert with a certificate created from ssl_certificate' do
|
|
251
|
+
expect(OpenSSL::X509::Certificate).to receive(:new).with(ssl_certificate).once.and_return cert
|
|
252
|
+
expect(store).to receive(:add_cert).once.with(cert)
|
|
253
|
+
expect(subject).to be_truthy
|
|
254
|
+
end
|
|
247
255
|
end
|
|
248
|
-
end
|
|
249
256
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
257
|
+
context 'when cert_file is given and ssl_certificate is not' do
|
|
258
|
+
let(:cert_file){ '/path/to/cert.pem' }
|
|
259
|
+
let(:ssl_certificate){ nil }
|
|
260
|
+
it 'calls store.add_file with cert_file' do
|
|
261
|
+
expect(store).to receive(:add_file).with(cert_file).once
|
|
262
|
+
expect(subject).to be_truthy
|
|
263
|
+
end
|
|
256
264
|
end
|
|
257
|
-
end
|
|
258
265
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
context 'when ssl_certificate is given' do
|
|
267
|
+
let(:cert_file){ nil }
|
|
268
|
+
let(:ssl_certificate){ "-----BEGIN CERTIFICATE----- MIIDUTCCAjmgAwIBAgIJAO4Lf1Rf2cciMA0GCSqGSIb3DQEBBQUAMDMxMTAvBgNV BAMTKGVjMi01NC05MS0yNDYtODQuY29tcHV0ZS0xLmFtYXpvbmF3cy5jb20wHhcN MTQxMDA4MjEwNTA5WhcNMjQxMDA1MjEwNTA5WjAzMTEwLwYDVQQDEyhlYzItNTQt OTEtMjQ2LTg0LmNvbXB1dGUtMS5hbWF6b25hd3MuY29tMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAx+OFANXNEYNsMR3Uvg4/72VG3LZO8yxrYaYzc3FZ NN3NpIOCZvRTC5S+OawsdEljHwfhdVoXdWNKgVJakSxsAnnaj11fA6XpfN60o6Fk i4q/BqwqgeNJjKAlElFsNz2scWFWRe49NHlj9qaq/yWZ8Cn0IeHy8j8F+jMek4zt dCSxVEayVG/k8RFmYCcluQc/1LuCjPiFwJU43AGkO+yvmOuYGivsNKY+54yuEZqF VDsjAjMsYXxgLx9y1F7Rq3CfeqY6IajR7pmmRup8/D9NyyyQuIML83mjTSvo0UYu rkdXPObd/m6gumscvXMl6SoJ5IPItvTA42MZqTaNzimF0QIDAQABo2gwZjBkBgNV HREEXTBbgglsb2NhbGhvc3SCBmNvbmp1coIcY29uanVyLW1hc3Rlci5pdHAuY29u anVyLm5ldIIoZWMyLTU0LTkxLTI0Ni04NC5jb21wdXRlLTEuYW1hem9uYXdzLmNv bTANBgkqhkiG9w0BAQUFAAOCAQEANk7P3ZEZHLgiTrLG13VAkm33FAvFzRG6akx1 jgNeRDgSaxRtrfJq3mnhsmD6hdvv+e6prPCFOjeEDheyCZyQDESdVEJBwytHVjnH dbvgMRaPm6OO8CyRyNjg3YcC36T//oQKOdAXXEcrtd0QbelBDYlKA7smJtznfhAb XypVdeS/6I4qvJi3Ckp5sQ1GszYhVXAvEeWeY59WwsTWYHLkzss9QShnigPyo3LY ZA5JVXofYi9DJ6VexP7sJNhCMrY2WnMpPcAOB9T7a6lcoXj6mWxvFys0xDIEOnc6 NGb+d47blphUKRZMAUZgYgFfMfmlyu1IXj03J8AuKtIMEwkXAA== -----END CERTIFICATE----- " }
|
|
262
269
|
let(:actual_certificate) {
|
|
263
270
|
<<-CERT
|
|
264
271
|
-----BEGIN CERTIFICATE-----
|
|
@@ -283,49 +290,49 @@ NGb+d47blphUKRZMAUZgYgFfMfmlyu1IXj03J8AuKtIMEwkXAA==
|
|
|
283
290
|
-----END CERTIFICATE-----
|
|
284
291
|
CERT
|
|
285
292
|
}
|
|
286
|
-
|
|
293
|
+
let(:cert){ double('cert') }
|
|
287
294
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
before do
|
|
296
|
+
expect(OpenSSL::X509::Certificate).to receive(:new).with(actual_certificate).at_least(:once).and_return cert
|
|
297
|
+
end
|
|
291
298
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
it 'calls store.add_cert with a certificate created from ssl_certificate' do
|
|
300
|
+
expect(store).to receive(:add_cert).with(cert).once
|
|
301
|
+
expect(subject).to be_truthy
|
|
302
|
+
end
|
|
296
303
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
304
|
+
it 'rescues from a StoreError with message "cert already in hash tabble"' do
|
|
305
|
+
expect(store).to receive(:add_cert).with(cert).once.and_raise(OpenSSL::X509::StoreError.new('cert already in hash table'))
|
|
306
|
+
expect(subject).to be_truthy
|
|
307
|
+
end
|
|
301
308
|
|
|
302
309
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
it 'does not rescue from other exceptions' do
|
|
311
|
+
exn = OpenSSL::X509::StoreError.new('some other message')
|
|
312
|
+
expect(store).to receive(:add_cert).with(cert).once.and_raise(exn)
|
|
313
|
+
expect{subject}.to raise_error exn
|
|
314
|
+
exn = ArgumentError.new('bad news')
|
|
315
|
+
expect(store).to receive(:add_cert).with(cert).once.and_raise(exn)
|
|
316
|
+
expect{subject}.to raise_error exn
|
|
317
|
+
end
|
|
310
318
|
end
|
|
311
|
-
end
|
|
312
319
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
320
|
+
context 'when given a store argument' do
|
|
321
|
+
let(:cert_file){ '/path/to/cert.pem' }
|
|
322
|
+
let(:ssl_certificate){ nil }
|
|
323
|
+
let(:alt_store){ double('alt store') }
|
|
324
|
+
subject{ Conjur.configuration.apply_cert_config! alt_store }
|
|
318
325
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
326
|
+
it 'uses that store instead' do
|
|
327
|
+
expect(alt_store).to receive(:add_file).with(cert_file).once
|
|
328
|
+
expect(subject).to be_truthy
|
|
329
|
+
end
|
|
322
330
|
end
|
|
323
|
-
end
|
|
324
331
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
332
|
+
context 'with two certificates in a string' do
|
|
333
|
+
let(:cert_file) { nil }
|
|
334
|
+
let(:ssl_certificate) do
|
|
335
|
+
"""-----BEGIN CERTIFICATE-----
|
|
329
336
|
MIIDPjCCAiagAwIBAgIVAKW1gdmOFrXt6xB0iQmYQ4z8Pf+kMA0GCSqGSIb3DQEB
|
|
330
337
|
CwUAMD0xETAPBgNVBAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDAS
|
|
331
338
|
BgNVBAMTC2N1a2UtbWFzdGVyMB4XDTE1MTAwNzE2MzAwNloXDTI1MTAwNDE2MzAw
|
|
@@ -367,12 +374,35 @@ BvYRjnTB2LSxfmSnkrCeFPmhE11bWVtsLIdrGIgtEMX0/s9xg58QuNnva1U3pJsW
|
|
|
367
374
|
RjvSxre4Xg2qlI9Laybb4oZ4g6DI8hRbL0VdFAsveg6SXg2RxgJcXeJUFw==
|
|
368
375
|
-----END CERTIFICATE-----
|
|
369
376
|
"""
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it 'adds both to the store' do
|
|
380
|
+
expect(store).to receive(:add_cert).twice
|
|
381
|
+
expect(subject).to be_truthy
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
context 'when cert file is not readable' do
|
|
388
|
+
let(:cert_file) { '/path/to/not_cert.pem' }
|
|
389
|
+
let(:ssl_certificate) { nil }
|
|
390
|
+
|
|
391
|
+
context 'raises ENOENT when cert file does not exist' do
|
|
392
|
+
let(:cert_exists) { false }
|
|
393
|
+
it 'raises the exception' do
|
|
394
|
+
expect{subject}.to raise_error(Errno::ENOENT)
|
|
395
|
+
end
|
|
370
396
|
end
|
|
371
397
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
398
|
+
context "raises EPERM when cert file does not have read permission" do
|
|
399
|
+
let(:cert_readable) {false}
|
|
400
|
+
it 'raises the exception' do
|
|
401
|
+
expect{subject}.to raise_error(Errno::EPERM)
|
|
402
|
+
end
|
|
375
403
|
end
|
|
404
|
+
|
|
376
405
|
end
|
|
406
|
+
|
|
377
407
|
end
|
|
378
408
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: conjur-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rafal Rzepecki
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rest-client
|
|
@@ -63,16 +63,16 @@ dependencies:
|
|
|
63
63
|
name: rake
|
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- -
|
|
66
|
+
- - ~>
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
68
|
+
version: '10.0'
|
|
69
69
|
type: :development
|
|
70
70
|
prerelease: false
|
|
71
71
|
version_requirements: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- -
|
|
73
|
+
- - ~>
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
75
|
+
version: '10.0'
|
|
76
76
|
- !ruby/object:Gem::Dependency
|
|
77
77
|
name: spork
|
|
78
78
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -303,6 +303,20 @@ dependencies:
|
|
|
303
303
|
- - '>='
|
|
304
304
|
- !ruby/object:Gem::Version
|
|
305
305
|
version: '0'
|
|
306
|
+
- !ruby/object:Gem::Dependency
|
|
307
|
+
name: fakefs
|
|
308
|
+
requirement: !ruby/object:Gem::Requirement
|
|
309
|
+
requirements:
|
|
310
|
+
- - '>='
|
|
311
|
+
- !ruby/object:Gem::Version
|
|
312
|
+
version: '0'
|
|
313
|
+
type: :development
|
|
314
|
+
prerelease: false
|
|
315
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
316
|
+
requirements:
|
|
317
|
+
- - '>='
|
|
318
|
+
- !ruby/object:Gem::Version
|
|
319
|
+
version: '0'
|
|
306
320
|
description: Conjur API
|
|
307
321
|
email:
|
|
308
322
|
- rafal@conjur.net
|