conjur-api 4.23.0 → 4.24.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: 91bb6f8e907814c4dd388fef05384202a9e51a29
4
- data.tar.gz: 4884d67d8ec68a02fdbabe297edeb1e8d95a6d88
3
+ metadata.gz: b6a400415b1a2ace3c70bfa63befe1155411e689
4
+ data.tar.gz: da597b98f100670f494de01da0c7ec3e14f23164
5
5
  SHA512:
6
- metadata.gz: 425b6e7ecbae6f9d79ee004a522ebe51d8ee091f452d3e6d30ff041a33dad04b15d33676b29179105e19f28b537be6325a0f951412a757236e07a1d03dee5954
7
- data.tar.gz: a4fb4478261d5f9a677c19c2b8103e1f5a9a78015b36dcf17780a1c8288c43de3e3f854b26e1dde1f17c237bde971ac1d9cd1e2544b2fafd195ee10c798f9cdc
6
+ metadata.gz: 7cd91330ebb3ca97d512e639ee35256dd79723b55528fef64290e4d2c5c863048d4667598604e61145de22316045fe6a13a2a7bc5ba156819f7c440b52c0d3c1
7
+ data.tar.gz: beb95a4206bf8ffed5d1bd8bee7a799d0d9984d7f7dffb729b3e29bb1832ed576e77c083e00988e0e6bb5e0e77e4e39a381cd360090946ba478fab9f7c12a579
@@ -1,3 +1,11 @@
1
+ # v4.24.0
2
+
3
+ * Add `Conjur::API#ldap_sync_now` (requires Conjur 4.7 or later).
4
+ * Don't trust the system clock and don't check token validity. Rely on the
5
+ server to verify the token instead, and only try to refresh if enough time
6
+ has passed locally (using monotonic clock for reference where available).
7
+ * Don't try refreshing the token if the required credentials are not available.
8
+
1
9
  # v4.23.0
2
10
 
3
11
  * Add `with_audit_roles` and `with_audit_resources` to `Conjur::API`
@@ -38,7 +38,6 @@ Gem::Specification.new do |gem|
38
38
  gem.add_development_dependency 'rdoc'
39
39
  gem.add_development_dependency 'yard'
40
40
  gem.add_development_dependency 'redcarpet'
41
- gem.add_development_dependency 'timecop'
42
41
  gem.add_development_dependency 'tins', '~> 1.6', '< 1.7.0'
43
42
  gem.add_development_dependency 'inch'
44
43
  end
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "4.23.0"
22
+ VERSION = "4.24.0"
23
23
  end
24
24
  end
@@ -45,6 +45,7 @@ require 'conjur/host-factory-api'
45
45
  require 'conjur/bootstrap'
46
46
  require 'conjur-api/version'
47
47
  require 'conjur/api/info'
48
+ require 'conjur/api/ldapsync'
48
49
 
49
50
  # Monkey patch RestClient::Request so it always uses
50
51
  # :ssl_cert_store. (RestClient::Resource uses Request to send
@@ -57,14 +58,14 @@ class RestClient::Request
57
58
  ssl_cert_store: OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
58
59
  }
59
60
  end
60
-
61
+
61
62
  def initialize args
62
63
  initialize_without_defaults default_args.merge(args)
63
64
  end
64
-
65
+
65
66
  end
66
67
 
67
-
68
+
68
69
  class RestClient::Resource
69
70
  include Conjur::Escape
70
71
  include Conjur::LogSource
@@ -119,11 +120,11 @@ class RestClient::Resource
119
120
  raise AuthorizationError.new("Authorization missing")
120
121
  end
121
122
  end
122
-
123
+
123
124
  def remote_ip
124
125
  options[:headers][:x_forwarded_for]
125
126
  end
126
-
127
+
127
128
  def conjur_privilege
128
129
  options[:headers][:x_conjur_privilege]
129
130
  end
@@ -0,0 +1,51 @@
1
+ #
2
+ # Copyright (C) 2016 Conjur Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ #
21
+
22
+ module Conjur
23
+ class API
24
+ # @!group LDAP Sync Service
25
+
26
+ # Trigger a LDAP sync with a given profile.
27
+
28
+ # @param [String] config_name Saved profile to run sync with
29
+ # @param [Boolean] dry_run Don't actually run sync, instead just report the state of the upstream LDAP.
30
+ # @param [String] format Requested MIME type of the response, either 'text/yaml' or 'application/json'
31
+ # @return [Hash] a hash mapping with keys 'ok' and 'result[:actions]'
32
+ def ldap_sync_now(config_name, format, dry_run)
33
+ opts = credentials.dup.tap{ |h|
34
+ h[:headers][:accept] = format
35
+ }
36
+
37
+ resp = RestClient::Resource.new(Conjur.configuration.appliance_url, opts)['ldap-sync']['sync'].post({
38
+ config_name: config_name,
39
+ dry_run: dry_run
40
+ })
41
+
42
+ if format == 'text/yaml'
43
+ resp.body
44
+ elsif format == 'application/json'
45
+ JSON.parse(resp.body)
46
+ end
47
+ end
48
+
49
+ # @!endgroup
50
+ end
51
+ end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2014 Conjur Inc
2
+ # Copyright (C) 2013-2016 Conjur Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  # this software and associated documentation files (the "Software"), to deal in
@@ -222,22 +222,14 @@ module Conjur
222
222
  self.class.host
223
223
  end
224
224
 
225
- # The token used to authenticate requests made with the api. The token will be fetched
226
- # if it hasn't already, or if it has expired. Accordingly, this method may raise a RestClient::Unauthorized
227
- # exception if the credentials are invalid.
228
- #
229
- # @note calling this method on an {Conjur::API} instance created with {Conjur::API.new_from_token} will have
230
- # undefined behavior if the token is expired.
225
+ # The token used to authenticate requests made with the api. The token will be fetched,
226
+ # if possible, when not present or about to expire. Accordingly, this
227
+ # method may raise a RestClient::Unauthorized exception if the credentials are invalid.
231
228
  #
232
229
  # @return [Hash] the authentication token as a Hash
233
230
  # @raise [RestClient::Unauthorized] if the username and api key are invalid.
234
231
  def token
235
- @token = nil unless token_valid?
236
-
237
- @token ||= Conjur::API.authenticate(@username, @api_key)
238
-
239
- validate_token
240
-
232
+ refresh_token if needs_token_refresh?
241
233
  return @token
242
234
  end
243
235
 
@@ -285,28 +277,35 @@ module Conjur
285
277
 
286
278
  private
287
279
 
288
- def token_valid?
289
- begin
290
- validate_token
291
- return true
292
- rescue Exception
293
- return false
294
- end
280
+
281
+ # Tries to refresh the token if possible.
282
+ #
283
+ # @return [Hash, false] false if the token couldn't be refreshed due to
284
+ # unavailable API key; otherwise, the new token.
285
+ def refresh_token
286
+ return false unless @api_key
287
+ @token_born = gettime
288
+ @token = Conjur::API.authenticate(@username, @api_key)
295
289
  end
296
290
 
297
- # Check to see if @token is defined, and whether it's expired
291
+ TOKEN_STALE = 5.minutes
292
+
293
+ # Checks if the token is old (or not present).
298
294
  #
299
- # @raise [Exception] if the token is invalid
300
- def validate_token
301
- fail "token not present" unless @token
302
-
303
- # Actual token expiration is 8 minutes, but why cut it so close
304
- expiration = 5.minutes
305
- lag = Time.now - Time.parse(@token['timestamp'])
306
- unless lag < expiration
307
- fail "obtained token is invalid: "\
308
- "token timestamp is #{@token['timestamp']}, #{lag} seconds ago"
309
- end
295
+ # @return [Boolean]
296
+ def needs_token_refresh?
297
+ !@token || ((token_age || 0) > TOKEN_STALE)
298
+ end
299
+
300
+ def gettime
301
+ Process.clock_gettime Process::CLOCK_MONOTONIC
302
+ rescue
303
+ # fall back to normal clock if there's no CLOCK_MONOTONIC
304
+ Time.now.to_f
305
+ end
306
+
307
+ def token_age
308
+ @token_born && (gettime - @token_born)
310
309
  end
311
310
  end
312
311
  end
@@ -25,7 +25,8 @@ module Conjur
25
25
  # The helpers are added as both class and isntance methods.
26
26
  module Escape
27
27
  module ClassMethods
28
- # URL escape the entire string. This is essentially the same as calling `CGI.escape str`.
28
+ # URL escape the entire string. This is essentially the same as calling `CGI.escape str`,
29
+ # and then substituting `%20` for `+`.
29
30
  #
30
31
  # @example
31
32
  # fully_escape 'foo/bar@baz'
@@ -19,9 +19,6 @@
19
19
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  #
21
21
  module Conjur
22
- class InvalidToken < Exception
23
- end
24
-
25
22
  # This class represents a {http://developer.conjur.net/reference/services/directory/user Conjur User}.
26
23
  class User < RestClient::Resource
27
24
  include ActsAsAsset
@@ -0,0 +1,54 @@
1
+ #
2
+ # Copyright (C) 2016 Conjur Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ #
21
+ require 'spec_helper'
22
+
23
+ describe Conjur::API, api: :dummy do
24
+ let(:appliance_url){ "http://example.com/api" }
25
+ let(:ldapsync_url){ "#{appliance_url}/ldap-sync/sync" }
26
+ let(:response_json){
27
+ {
28
+ :okay => true,
29
+ :result => {
30
+ :actions => [
31
+ "Create user 'Guest'\n Set annotation 'ldap-sync/source'\n Set annotation 'ldap-sync/upstream-dn'"
32
+ ]
33
+ }
34
+ }
35
+ }
36
+ let(:response){ double('response', body: response_json.to_json) }
37
+
38
+ before do
39
+ allow(Conjur.configuration).to receive(:appliance_url).and_return appliance_url
40
+ allow(Conjur::API).to receive_messages(ldap_sync_now: ldapsync_url)
41
+ end
42
+
43
+ describe "#ldap_sync_now" do
44
+ it "POSTs /sync" do
45
+ expect_request(
46
+ url: ldapsync_url,
47
+ method: :post,
48
+ headers: credentials[:headers],
49
+ payload: {config_name: 'default', dry_run: true}
50
+ ).and_return response
51
+ api.ldap_sync_now('default', 'application/json', true)
52
+ end
53
+ end
54
+ end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'timecop'
3
2
 
4
3
  shared_examples_for "API endpoint" do
5
4
  before { Conjur.configuration = Conjur::Configuration.new }
@@ -222,24 +221,67 @@ describe Conjur::API do
222
221
  end
223
222
  end
224
223
 
225
- shared_context logged_in: true do
224
+ shared_context "logged in", logged_in: true do
226
225
  let(:login) { "bob" }
227
226
  let(:token) { { 'data' => login, 'timestamp' => Time.now.to_s } }
228
- subject { api }
229
227
  let(:remote_ip) { nil }
230
228
  let(:api_args) { [ token, remote_ip ].compact }
231
- let(:api) { Conjur::API.new_from_token(*api_args) }
229
+ subject(:api) { Conjur::API.new_from_token(*api_args) }
232
230
  let(:account) { 'some-account' }
233
231
  before { allow(Conjur::Core::API).to receive_messages conjur_account: account }
234
232
  end
235
233
 
236
- context "credential handling", logged_in: true do
237
- context "from token" do
238
- describe '#token' do
239
- subject { super().token }
240
- it { is_expected.to eq(token) }
234
+ shared_context "logged in with an API key", logged_in: :api_key do
235
+ include_context "logged in"
236
+ let(:api_key) { "theapikey" }
237
+ let(:api_args) { [ login, api_key, remote_ip ].compact }
238
+ subject(:api) { Conjur::API.new_from_key(*api_args) }
239
+ end
240
+
241
+ def time_travel delta
242
+ allow(api).to receive(:gettime).and_wrap_original do |m|
243
+ m[] + delta
244
+ end
245
+ end
246
+
247
+ describe '#token' do
248
+ context 'with API key available', logged_in: :api_key do
249
+ it "authenticates to get a token" do
250
+ expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return token
251
+
252
+ expect(api.instance_variable_get("@token")).to eq(nil)
253
+ expect(api.token).to eq(token)
254
+ expect(api.credentials).to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login })
241
255
  end
242
256
 
257
+ it "fetches a new token if old" do
258
+ allow(Conjur::API).to receive(:authenticate).with(login, api_key).and_return token
259
+ expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
260
+
261
+ time_travel 6.minutes
262
+ new_token = token.merge "timestamp" => Time.now.to_s
263
+
264
+ expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return new_token
265
+ expect(api.token).to eq(new_token)
266
+ end
267
+ end
268
+
269
+ context 'with no API key available', logged_in: true do
270
+ it "returns the token used to create it" do
271
+ expect(api.token).to eq token
272
+ end
273
+
274
+ it "doesn't try to refresh an old token" do
275
+ expect(Conjur::API).not_to receive :authenticate
276
+ api.token # vivify
277
+ time_travel 6.minutes
278
+ expect { api.token }.not_to raise_error
279
+ end
280
+ end
281
+ end
282
+
283
+ context "credential handling", logged_in: true do
284
+ context "from token" do
243
285
  describe '#credentials' do
244
286
  subject { super().credentials }
245
287
  it { is_expected.to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login }) }
@@ -260,44 +302,6 @@ describe Conjur::API do
260
302
  end
261
303
  end
262
304
  end
263
-
264
-
265
- context "from api key", logged_in: true do
266
- let(:api_key) { "theapikey" }
267
- let(:api_args) { [ login, api_key, remote_ip ].compact }
268
- let(:api) { Conjur::API.new_from_key(*api_args) }
269
- let(:remote_ip) { nil }
270
- subject { api }
271
-
272
- it("should authenticate to get a token") do
273
- expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return token
274
-
275
- expect(api.instance_variable_get("@token")).to eq(nil)
276
- expect(api.token).to eq(token)
277
- expect(api.credentials).to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login })
278
- end
279
-
280
- it("checks if the token is fresh") do
281
- expired_token = token.merge 'timestamp' => 10.minutes.ago.to_s
282
- expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return expired_token
283
-
284
- expect(api.instance_variable_get("@token")).to eq(nil)
285
- expect { api.token }.to raise_error /obtained token is invalid/
286
- end
287
-
288
- context "with an expired token" do
289
- it "fetches a new one" do
290
- allow(Conjur::API).to receive(:authenticate).with(login, api_key).and_return token
291
- expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
292
-
293
- Timecop.travel Time.now + 6.minutes
294
- new_token = token.merge "timestamp" => Time.now.to_s
295
-
296
- expect(Conjur::API).to receive(:authenticate).with(login, api_key).and_return new_token
297
- expect(api.token).to eq(new_token)
298
- end
299
- end
300
- end
301
305
 
302
306
  context "from logged-in RestClient::Resource" do
303
307
  let (:authz_header) { %Q{Token token="#{token_encoded}"} }
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.23.0
4
+ version: 4.24.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-04-22 00:00:00.000000000 Z
12
+ date: 2016-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -269,20 +269,6 @@ dependencies:
269
269
  - - '>='
270
270
  - !ruby/object:Gem::Version
271
271
  version: '0'
272
- - !ruby/object:Gem::Dependency
273
- name: timecop
274
- requirement: !ruby/object:Gem::Requirement
275
- requirements:
276
- - - '>='
277
- - !ruby/object:Gem::Version
278
- version: '0'
279
- type: :development
280
- prerelease: false
281
- version_requirements: !ruby/object:Gem::Requirement
282
- requirements:
283
- - - '>='
284
- - !ruby/object:Gem::Version
285
- version: '0'
286
272
  - !ruby/object:Gem::Dependency
287
273
  name: tins
288
274
  requirement: !ruby/object:Gem::Requirement
@@ -361,6 +347,7 @@ files:
361
347
  - lib/conjur/api/hosts.rb
362
348
  - lib/conjur/api/info.rb
363
349
  - lib/conjur/api/layers.rb
350
+ - lib/conjur/api/ldapsync.rb
364
351
  - lib/conjur/api/pubkeys.rb
365
352
  - lib/conjur/api/resources.rb
366
353
  - lib/conjur/api/roles.rb
@@ -414,6 +401,7 @@ files:
414
401
  - spec/api/hosts_spec.rb
415
402
  - spec/api/info_spec.rb
416
403
  - spec/api/layer_spec.rb
404
+ - spec/api/ldapsync_spec.rb
417
405
  - spec/api/pubkeys_spec.rb
418
406
  - spec/api/resources_spec.rb
419
407
  - spec/api/roles_spec.rb
@@ -490,6 +478,7 @@ test_files:
490
478
  - spec/api/hosts_spec.rb
491
479
  - spec/api/info_spec.rb
492
480
  - spec/api/layer_spec.rb
481
+ - spec/api/ldapsync_spec.rb
493
482
  - spec/api/pubkeys_spec.rb
494
483
  - spec/api/resources_spec.rb
495
484
  - spec/api/roles_spec.rb