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 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