conjur-api 4.28.1 → 4.29.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|