conjur-api 4.23.0 → 4.24.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: 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