conjur-api 5.3.3 → 5.3.4

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
  SHA256:
3
- metadata.gz: 4d068a6fcf42161573c1d317260549dd7e30001e1c53d9edeb36e8c8646f7db7
4
- data.tar.gz: 0ea117aee05921d67c2feef6b863fae286f5556833134f410dbab73a18cc13b9
3
+ metadata.gz: 476ea2f5b5e2a375363e03e6c4659f5a425837b5e3036f41ae5aea208c56f781
4
+ data.tar.gz: 973be7e50f9a8a86c78770125723e42460e2264ceb7d71823fbd5d4962a31195
5
5
  SHA512:
6
- metadata.gz: bda454b83559d845aad1a5b13e824937f9574420c9c14d0743fd93c56f79ac6de462648901bc3c9d7612ad6243099a659f1292d9b4fe7628865e6f6ca8dfa562
7
- data.tar.gz: a6e5d7397c882d4d43ee95eead36bf51519fa5d6ea7c754b0c2648388769b9800180df8757c1ac5d3e968738e6e92dd013ba71c08686f2a3e6f85b1700556558
6
+ metadata.gz: 5c1cb2ded26fe6dfd44992ef4a81e5e71a01551f2874c1045a66fb556da05b55268cceef5124323dfbe14c7e032da3382e0d48cf21732a443a3c52e70af53b38
7
+ data.tar.gz: 58e061632c5c072134f5d2a23dab0103d73790750c054d5f6167ee0fa239598908130fc921aa3f74b9f57a93ced1092ec6165b55ae54728b3e16114baca486f1
@@ -6,11 +6,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [5.3.4] - 2020-10-29
10
+
11
+ ### Changed
12
+ - When rotating the currently logged in user's/host's API key, we now explictily
13
+ prevent use of `resource(<own_id>).rotate_api_key` for that action as the
14
+ `Conjur::API.rotate_api_key` should be used instead for that. This change is a
15
+ downstream enforcement of the stricter key rotation requirements on the server
16
+ covered by [this](https://github.com/cyberark/conjur/security/advisories/GHSA-qhjf-g9gm-64jq)
17
+ security bulletin.
18
+ [cyberark/conjur-api-ruby#181](https://github.com/cyberark/conjur-api-ruby/issues/181)
19
+
9
20
  ## [5.3.3] - 2020-08-18
10
21
  ### Changed
11
22
  - Release process is updated to ensure that the published Ruby Gem matches a tag in this repository,
12
23
  so that consumers of this gem can always reference the correct source code included in any given version.
13
- [cyberark/conjur-api-ruby](https://github.com/cyberark/conjur-api-ruby/issues/173)
24
+ [cyberark/conjur-api-ruby#173](https://github.com/cyberark/conjur-api-ruby/issues/173)
14
25
 
15
26
  ## 5.3.2 - 2018-09-24
16
27
  ### Added
@@ -321,7 +332,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
321
332
 
322
333
  ## [2.0.0] - 2013-13-12
323
334
 
324
- [Unreleased]: https://github.com/cyberark/conjur-api-ruby/compare/v5.3.3...HEAD
335
+ [Unreleased]: https://github.com/cyberark/conjur-api-ruby/compare/v5.3.4...HEAD
336
+ [5.3.4]: https://github.com/cyberark/conjur-api-ruby/compare/v5.3.3...v5.3.4
325
337
  [5.3.3]: https://github.com/cyberark/conjur-api-ruby/compare/v5.3.1...v5.3.3
326
338
  [5.3.1]: https://github.com/cyberark/conjur-api-ruby/compare/v5.3.0...v5.3.1
327
339
  [5.3.0]: https://github.com/cyberark/conjur-api-ruby/compare/v5.1.0...v5.3.0
@@ -1,20 +1,50 @@
1
- Feature: Display Host object fields.
1
+ Feature: Host object
2
2
 
3
- Background:
3
+ Scenario: API key of a newly created host is available and valid
4
4
  Given a new host
5
-
6
- Scenario: API key of a newly created host is available and valid.
7
- Then I run the code:
5
+ Then I can run the code:
8
6
  """
9
7
  expect(@host.exists?).to be(true)
10
8
  expect(@host.api_key).to be
11
9
  Conjur::API.new_from_key(@host.login, @host.api_key).token
12
10
  """
13
11
 
14
- Scenario: API key of a a host can be rotated.
15
- Then I run the code:
12
+ # Rotation of own API key should be done via `Conjur::API.rotate_api_key()`
13
+ Scenario: Host's own API key cannot be rotated with an API key
14
+ Given a new host
15
+ Then this code should fail with "You cannot rotate your own API key via this method"
16
16
  """
17
17
  host = Conjur::API.new_from_key(@host.login, @host.api_key).resource(@host.id)
18
- api_key = host.rotate_api_key
19
- Conjur::API.new_from_key(@host.login, api_key).token
18
+ host.rotate_api_key
19
+ """
20
+
21
+ # Rotation of own API key should be done via `Conjur::API.rotate_api_key()`
22
+ Scenario: Host's own API key cannot be rotated with a token
23
+ Given a new host
24
+ Then this code should fail with "You cannot rotate your own API key via this method"
25
+ """
26
+ token = Conjur::API.new_from_key(@host.login, @host.api_key).token
27
+
28
+ host = Conjur::API.new_from_token(token).resource(@host.id)
29
+ host.rotate_api_key
30
+ """
31
+
32
+ Scenario: Delegated host's API key can be rotated with an API key
33
+ Given a new delegated host
34
+ Then I can run the code:
35
+ """
36
+ delegated_host_resource = Conjur::API.new_from_key(@host_owner.login, @host_owner_api_key).resource(@host.id)
37
+ api_key = delegated_host_resource.rotate_api_key
38
+ Conjur::API.new_from_key(delegated_host_resource.login, api_key).token
39
+ """
40
+
41
+ Scenario: Delegated host's API key can be rotated with a token
42
+ Given a new delegated host
43
+ Then I can run the code:
44
+ """
45
+ token = Conjur::API.new_from_key(@host_owner.login, @host_owner_api_key).token
46
+
47
+ delegated_host_resource = Conjur::API.new_from_token(token).resource(@host.id)
48
+ api_key = delegated_host_resource.rotate_api_key
49
+ Conjur::API.new_from_key(delegated_host_resource.login, api_key).token
20
50
  """
@@ -1,7 +1,18 @@
1
- When(/^I(?: can)? run the code:$/) do |code|
1
+ Then(/^I(?: can)? run the code:$/) do |code|
2
2
  @result = eval(code).tap do |result|
3
- if ENV['DEBUG']
4
- puts result
3
+ puts result if ENV['DEBUG']
4
+ end
5
+ end
6
+
7
+ Then(/^this code should fail with "([^"]*)"$/) do |error_msg, code|
8
+ begin
9
+ @result = eval(code)
10
+ rescue Exception => exc
11
+ if not exc.message =~ %r{#{error_msg}}
12
+ fail "'#{error_msg}' was not found in '#{exc.message}'"
5
13
  end
14
+ else
15
+ puts @result if ENV['DEBUG']
16
+ fail "The provided block did not raise an error"
6
17
  end
7
18
  end
@@ -13,6 +13,25 @@ Given(/^a new user$/) do
13
13
  expect(@user_api_key).to be
14
14
  end
15
15
 
16
+ Given(/^a new delegated user$/) do
17
+ # Create a new host that is owned by that user
18
+ step 'a new user'
19
+ @user_owner = @user
20
+ @user_owner_id = @user_id
21
+ @user_owner_api_key = @user_api_key
22
+
23
+ # Create a new user that is owned by the user created earlier
24
+ @user_id = "user-#{random_hex}"
25
+ response = $conjur.load_policy 'root', <<-POLICY
26
+ - !user
27
+ id: #{@user_id}
28
+ owner: !user #{@user_owner_id}
29
+ POLICY
30
+ @user = $conjur.resource("cucumber:user:#{@user_id}")
31
+ @user_api_key = response.created_roles["cucumber:user:#{@user_id}"]['api_key']
32
+ expect(@user_api_key).to be
33
+ end
34
+
16
35
  Given(/^a new group$/) do
17
36
  @group_id = "group-#{random_hex}"
18
37
  response = $conjur.load_policy 'root', <<-POLICY
@@ -33,3 +52,24 @@ Given(/^a new host$/) do
33
52
  @host = $conjur.resource("cucumber:host:#{@host_id}")
34
53
  @host.attributes['api_key'] = @host_api_key
35
54
  end
55
+
56
+ Given(/^a new delegated host$/) do
57
+ # Create an owner user
58
+ step 'a new user'
59
+ @host_owner = @user
60
+ @host_owner_id = @user_id
61
+ @host_owner_api_key = @user_api_key
62
+
63
+ # Create a new host that is owned by that user
64
+ @host_id = "app-#{random_hex}"
65
+ response = $conjur.load_policy 'root', <<-POLICY
66
+ - !host
67
+ id: #{@host_id}
68
+ owner: !user #{@host_owner_id}
69
+ POLICY
70
+
71
+ @host_api_key = response.created_roles["cucumber:host:#{@host_id}"]['api_key']
72
+ expect(@host_api_key).to be
73
+ @host = $conjur.resource("cucumber:host:#{@host_id}")
74
+ @host.attributes['api_key'] = @host_api_key
75
+ end
@@ -1,17 +1,58 @@
1
- Feature: Display User object fields.
1
+ Feature: User object
2
2
 
3
3
  Background:
4
- Given a new user
5
4
 
6
- Scenario: User has a uidnumber.
7
- Then I run the code:
5
+ Scenario: User has a uidnumber
6
+ Given a new user
7
+ Then I can run the code:
8
8
  """
9
9
  @user.uidnumber
10
10
  """
11
11
  Then the result should be "1000"
12
12
 
13
- Scenario: Logged-in user is the current_role.
14
- Then I run the code:
13
+ Scenario: Logged-in user is the current_role
14
+ Given a new user
15
+ Then I can run the code:
15
16
  """
16
17
  expect($conjur.current_role(Conjur.configuration.account).id.to_s).to eq("cucumber:user:admin")
17
18
  """
19
+
20
+ # Rotation of own API key should be done via `Conjur::API.rotate_api_key()`
21
+ Scenario: User's own API key cannot be rotated with an API key
22
+ Given a new user
23
+ Then this code should fail with "You cannot rotate your own API key via this method"
24
+ """
25
+ user = Conjur::API.new_from_key(@user.login, @user_api_key).resource(@user.id)
26
+ user.rotate_api_key
27
+ """
28
+
29
+ # Rotation of own API key should be done via `Conjur::API.rotate_api_key()`
30
+ Scenario: User's own API key cannot be rotated with a token
31
+ Given a new user
32
+ Then this code should fail with "You cannot rotate your own API key via this method"
33
+ """
34
+ token = Conjur::API.new_from_key(@user.login, @user_api_key).token
35
+
36
+ user = Conjur::API.new_from_token(token).resource(@user.id)
37
+ user.rotate_api_key
38
+ """
39
+
40
+ Scenario: Delegated user's API key can be rotated with an API key
41
+ Given a new delegated user
42
+ Then I can run the code:
43
+ """
44
+ delegated_user_resource = Conjur::API.new_from_key(@user_owner.login, @user_owner_api_key).resource(@user.id)
45
+ api_key = delegated_user_resource.rotate_api_key
46
+ Conjur::API.new_from_key(delegated_user_resource.login, api_key).token
47
+ """
48
+
49
+ Scenario: Delegated user's API key can be rotated with a token
50
+ Given a new delegated user
51
+ Then I can run the code:
52
+ """
53
+ token = Conjur::API.new_from_key(@user_owner.login, @user_owner_api_key).token
54
+
55
+ delegated_user_resource = Conjur::API.new_from_token(token).resource(@user.id)
56
+ api_key = delegated_user_resource.rotate_api_key
57
+ Conjur::API.new_from_key(delegated_user_resource.login, api_key).token
58
+ """
@@ -1,4 +1,4 @@
1
- # Copyright 2013-2017 Conjur Inc.
1
+ # Copyright 2013-2020 Conjur Inc.
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
4
4
  # this software and associated documentation files (the "Software"), to deal in
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "5.3.3"
22
+ VERSION = "5.3.4"
23
23
  end
24
24
  end
@@ -52,12 +52,16 @@ module Conjur
52
52
  # @note You will not be able to access the API key returned by this method later, so you should
53
53
  # probably hang onto it it.
54
54
  #
55
- # @note You cannot rotate your own API key with this method. To do so, use `Conjur::API.rotate_api_key`
55
+ # @note You cannot rotate your own API key with this method. To do so, use `Conjur::API.rotate_api_key`.
56
56
  #
57
57
  # @note This feature requires a Conjur appliance running version 4.6 or higher.
58
58
  #
59
59
  # @return [String] the new API key for this user.
60
60
  def rotate_api_key
61
+ if login == username
62
+ raise 'You cannot rotate your own API key via this method. To do so, use `Conjur::API.rotate_api_key`'
63
+ end
64
+
61
65
  url_for(:authn_rotate_api_key, credentials, account, id).put("").body
62
66
  end
63
67
  end
@@ -20,7 +20,7 @@ module Conjur
20
20
  class API
21
21
  include QueryString
22
22
  include BuildObject
23
-
23
+
24
24
  #@!group Resources
25
25
 
26
26
  # Find a resource by its id.
@@ -84,7 +84,7 @@ module Conjur
84
84
  def resources options = {}
85
85
  options = { host: Conjur.configuration.core_url, credentials: credentials }.merge options
86
86
  options[:account] ||= Conjur.configuration.account
87
-
87
+
88
88
  host, credentials, account, kind = options.values_at(*[:host, :credentials, :account, :kind])
89
89
  fail ArgumentError, "host and account are required" unless [host, account].all?
90
90
  %w(host credentials account kind).each do |name|
@@ -169,7 +169,7 @@ module Conjur
169
169
  def ldap_sync_policy(credentials, config_name)
170
170
  RestClient::Resource.new(Conjur.configuration.core_url, credentials)['ldap-sync']["policy?config_name=#{fully_escape(config_name)}"]
171
171
  end
172
-
172
+
173
173
  private
174
174
 
175
175
  def resource_annotations resource
@@ -123,19 +123,21 @@ module Conjur
123
123
  #
124
124
  # @return [String] the api key, or nil if this instance was created from a token.
125
125
  attr_reader :api_key
126
-
126
+
127
127
  #@!attribute [r] remote_ip
128
128
  # An optional IP address to be recorded in the audit record for any actions performed by this API instance.
129
129
  attr_reader :remote_ip
130
130
 
131
131
  # The name of the user as which this api instance is authenticated. This is available whether the api
132
- # instance was created from credentials or an authentication token.
132
+ # instance was created from credentials or an authentication token. If the instance was created from
133
+ # credentials, we will use that value directly otherwise we will attempt to extract the username from
134
+ # the token (either the old-style data field or the new-style JWT `sub` field).
133
135
  #
134
136
  # @return [String] the login of the current user.
135
137
  def username
136
- @username || token['data']
138
+ @username || token['data'] || jwt_username(token)
137
139
  end
138
-
140
+
139
141
  # @api private
140
142
  # used to delegate to host providing subclasses.
141
143
  # @return [String] the host
@@ -213,7 +215,7 @@ module Conjur
213
215
  @account = account
214
216
  @username = username
215
217
  @api_key = api_key
216
-
218
+
217
219
  update_token_born
218
220
  end
219
221
 
@@ -323,6 +325,18 @@ module Conjur
323
325
 
324
326
  private
325
327
 
328
+ # Tries to get the username (subject) from a JWT API token by examining
329
+ # its content.
330
+ #
331
+ # @return [String] of the 'sub' payload field from the JWT if present,
332
+ # otherwise return nil
333
+ def jwt_username raw_token
334
+ return nil unless raw_token
335
+ return nil unless raw_token.include? 'payload'
336
+
337
+ JSON.parse(Base64.strict_decode64(raw_token["payload"]))["sub"]
338
+ end
339
+
326
340
  # Tries to refresh the token if possible.
327
341
  #
328
342
  # @return [Hash, false] false if the token couldn't be refreshed due to
@@ -20,9 +20,9 @@ module Conjur
20
20
  include LogSource
21
21
  include BuildObject
22
22
  include Routing
23
-
23
+
24
24
  attr_reader :id, :credentials
25
-
25
+
26
26
  def initialize id, credentials
27
27
  @id = Id.new id
28
28
  @credentials = credentials
@@ -34,10 +34,18 @@ module Conjur
34
34
  }
35
35
  end
36
36
 
37
- def account; id.account; end
38
- def kind; id.kind; end
39
- def identifier; id.identifier; end
40
-
37
+ def account
38
+ id.account
39
+ end
40
+
41
+ def kind
42
+ id.kind
43
+ end
44
+
45
+ def identifier
46
+ id.identifier
47
+ end
48
+
41
49
  def username
42
50
  credentials[:username] or raise "No username found in credentials"
43
51
  end
@@ -45,6 +53,5 @@ module Conjur
45
53
  def inspect
46
54
  "<#{self.class.name} id='#{id.to_s}'>"
47
55
  end
48
-
49
56
  end
50
57
  end
@@ -60,11 +60,11 @@ describe Conjur::API do
60
60
  context "after expiration" do
61
61
  it 'it reads a new token' do
62
62
  expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
63
-
63
+
64
64
  time_travel 6.minutes
65
65
  new_token = token.merge "timestamp" => Time.now.to_s
66
66
  write_token new_token
67
-
67
+
68
68
  expect(api.token).to eq(new_token)
69
69
  end
70
70
  end
@@ -85,10 +85,10 @@ describe Conjur::API do
85
85
  it 'by refreshing' do
86
86
  allow(Conjur::API).to receive(:authenticate).with(login, api_key, account: account).and_return token
87
87
  expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
88
-
88
+
89
89
  time_travel 6.minutes
90
90
  new_token = token.merge "timestamp" => Time.now.to_s
91
-
91
+
92
92
  expect(Conjur::API).to receive(:authenticate).with(login, api_key, account: account).and_return new_token
93
93
  expect(api.token).to eq(new_token)
94
94
  end
@@ -118,7 +118,7 @@ describe Conjur::API do
118
118
  subject { super().credentials }
119
119
  it { is_expected.to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login }) }
120
120
  end
121
-
121
+
122
122
  context "with remote_ip" do
123
123
  let(:remote_ip) { "66.0.0.1" }
124
124
  describe '#credentials' do
@@ -153,7 +153,7 @@ describe Conjur::API do
153
153
  context 'basic functioning' do
154
154
  it_behaves_like 'it can clone itself'
155
155
  end
156
-
156
+
157
157
  context "forwarded for" do
158
158
  let(:forwarded_for_header) { "66.0.0.1" }
159
159
  let(:headers) { base_headers.merge(x_forwarded_for: forwarded_for_header) }
@@ -172,6 +172,55 @@ describe Conjur::API do
172
172
  end
173
173
  end
174
174
 
175
+ describe "#username" do
176
+ let(:jwt_payload) do
177
+ 'eyJzdWIiOiJ1c2VyLTlhYjBiYmZiOWJlNjA5Yzk2ZjUyN2Y1YiIsImlhdCI6MTYwMzQ5MDA4MH0='
178
+ end
179
+
180
+ let(:jwt_header) do
181
+ 'eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiI2MWZjOGRiZDM4MjA4NDll' \
182
+ 'ZDI4YTZhYTAwMzFjNjM5MjkxZjJmMDQzNDVjYTU0MWI5NzUxMGQ5NjkyM2I3NDlmIn0='
183
+ end
184
+
185
+ let(:conjur_token) do
186
+ {
187
+ 'data' => 'conjur-user-1234',
188
+ 'timestamp' => Time.now.to_s
189
+ }
190
+ end
191
+
192
+ let(:jwt_token) do
193
+ {
194
+ 'protected' => jwt_header,
195
+ 'payload' => jwt_payload,
196
+ }
197
+ end
198
+
199
+ it "can correctly extract the username from old Conjur token" do
200
+ expect(Conjur::API.new_from_token(conjur_token).username).to(
201
+ eq('conjur-user-1234')
202
+ )
203
+ end
204
+
205
+ context 'when using JWT token' do
206
+ it "can correctly extract username" do
207
+ expect(Conjur::API.new_from_token(jwt_token).username).to(
208
+ eq('user-9ab0bbfb9be609c96f527f5b')
209
+ )
210
+ end
211
+
212
+ it "returns nil when JWT token has no payload field" do
213
+ no_payload_jwt_token = { 'protected' => jwt_header }
214
+ expect(Conjur::API.new_from_token(no_payload_jwt_token).username).to be_nil
215
+ end
216
+
217
+ it "returns nil when JWT token has no 'sub' field in payload" do
218
+ no_sub_token = { 'payload' => 'eyJpYXQiOjE2MDM0OTAwODB9' }
219
+ expect(Conjur::API.new_from_token(no_sub_token).username).to be_nil
220
+ end
221
+ end
222
+ end
223
+
175
224
  describe "#current_role", logged_in: true do
176
225
  context "when logged in as user" do
177
226
  let(:login) { 'joerandom' }
@@ -10,5 +10,4 @@ describe Conjur::BaseObject do
10
10
  expect(base_obj.inspect).to include("id='#{id_str}'")
11
11
  expect(base_obj.inspect).to include(Conjur::BaseObject.name)
12
12
  end
13
-
14
13
  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: 5.3.3
4
+ version: 5.3.4
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: 2020-08-19 00:00:00.000000000 Z
12
+ date: 2020-10-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client