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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f57acfdd94157de9609e46b399c12484591d13c2
4
- data.tar.gz: a4ecc120203d25a8cfd56d9b01894a61e71d13cd
3
+ metadata.gz: f0564b6aa766b949e956c83932c25994999c8e03
4
+ data.tar.gz: 03be39ecd89a7b370115ce168c4b3c54acd45b09
5
5
  SHA512:
6
- metadata.gz: ff668011c790785e3c80eeeccac8a85ee22e6337030f96c58bb69f9c012bce35044c96d33b47ea52ee8d203c65c79f2aebaf5e24355c7f1646eef93cbd947f1c
7
- data.tar.gz: 547ae4223549ecf0150f861ec7e6736e90933219d9f22f122573f7745dedee3218ebf911b36eb44523bcfd3acad0ca99677582262ee3b27ab7fff9d97e7aa432
6
+ metadata.gz: d04e0fb688351001e486bc01613ecd5e1cd4ec66e7790f0ed802b9a7d4eb5280fef68498cf720ebe4a3cfc5a270e190a2e970fea8c9419cec16dbd77060b69d1
7
+ data.tar.gz: c64f11b333a74bfa8f5af3ad0985daa7b25f82d55db7514f00589f210be408d04079792b7a5ff8efe23922a5967e04e8759457b62c4fd8209b02d6daf7984232
@@ -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
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- #ruby=ruby-2.1.5
3
+ #ruby=ruby-2.2.5
4
4
  #ruby-gemset=conjur-api
5
5
 
6
6
  # Specify your gem's dependencies in conjur-api.gemspec
@@ -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
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash -ex
2
2
 
3
- CONJUR_VERSION=${CONJUR_VERSION:-"4.8"}
3
+ CONJUR_VERSION=${CONJUR_VERSION:-"4.9"}
4
4
  DOCKER_IMAGE=${DOCKER_IMAGE:-"registry.tld/conjur-appliance-cuke-master:$CONJUR_VERSION-stable"}
5
5
  NOKILL=${NOKILL:-"0"}
6
6
  PULL=${PULL:-"1"}
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "4.28.1"
22
+ VERSION = "4.29.0"
23
23
  end
24
24
  end
@@ -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
- return false unless @api_key
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 || ((token_age || 0) > TOKEN_STALE)
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
@@ -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
@@ -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 "when neither cert_file or ssl_certificate is present" do
229
- let(:cert_file){ nil }
230
- let(:ssl_certificate){ nil }
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
- it 'does nothing to the store' do
233
- expect(store).to_not receive(:add_file)
234
- expect(store).to_not receive(:add_cert)
235
- expect(subject).to be_falsey
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
- context 'when both are given' do
240
- let(:cert_file){ '/path/to/cert.pem' }
241
- let(:ssl_certificate){ "-----BEGIN CERTIFICATE-----\nfoo\n-----END CERTIFICATE-----\n" }
242
- let(:cert){ double('certificate') }
243
- it 'calls store.add_cert with a certificate created from ssl_certificate' do
244
- expect(OpenSSL::X509::Certificate).to receive(:new).with(ssl_certificate).once.and_return cert
245
- expect(store).to receive(:add_cert).once.with(cert)
246
- expect(subject).to be_truthy
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
- context 'when cert_file is given and ssl_certificate is not' do
251
- let(:cert_file){ '/path/to/cert.pem' }
252
- let(:ssl_certificate){ nil }
253
- it 'calls store.add_file with cert_file' do
254
- expect(store).to receive(:add_file).with(cert_file).once
255
- expect(subject).to be_truthy
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
- context 'when ssl_certificate is given' do
260
- let(:cert_file){ nil }
261
- 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----- " }
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
- let(:cert){ double('cert') }
293
+ let(:cert){ double('cert') }
287
294
 
288
- before do
289
- expect(OpenSSL::X509::Certificate).to receive(:new).with(actual_certificate).at_least(:once).and_return cert
290
- end
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
- it 'calls store.add_cert with a certificate created from ssl_certificate' do
293
- expect(store).to receive(:add_cert).with(cert).once
294
- expect(subject).to be_truthy
295
- end
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
- it 'rescues from a StoreError with message "cert already in hash tabble"' do
298
- expect(store).to receive(:add_cert).with(cert).once.and_raise(OpenSSL::X509::StoreError.new('cert already in hash table'))
299
- expect(subject).to be_truthy
300
- end
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
- it 'does not rescue from other exceptions' do
304
- exn = OpenSSL::X509::StoreError.new('some other message')
305
- expect(store).to receive(:add_cert).with(cert).once.and_raise(exn)
306
- expect{subject}.to raise_error exn
307
- exn = ArgumentError.new('bad news')
308
- expect(store).to receive(:add_cert).with(cert).once.and_raise(exn)
309
- expect{subject}.to raise_error exn
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
- context 'when given a store argument' do
314
- let(:cert_file){ '/path/to/cert.pem' }
315
- let(:ssl_certificate){ nil }
316
- let(:alt_store){ double('alt store') }
317
- subject{ Conjur.configuration.apply_cert_config! alt_store }
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
- it 'uses that store instead' do
320
- expect(alt_store).to receive(:add_file).with(cert_file).once
321
- expect(subject).to be_truthy
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
- context 'with two certificates in a string' do
326
- let(:cert_file) { nil }
327
- let(:ssl_certificate) do
328
- """-----BEGIN CERTIFICATE-----
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
- it 'adds both to the store' do
373
- expect(store).to receive(:add_cert).twice
374
- expect(subject).to be_truthy
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.28.1
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: 2016-11-30 00:00:00.000000000 Z
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