conjur-api 4.22.1 → 4.23.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/conjur-api.gemspec +1 -0
- data/features/audit_resources.feature +15 -0
- data/features/audit_roles.feature +15 -0
- data/features/step_definitions/api_steps.rb +33 -0
- data/features/step_definitions/cli_steps.rb +5 -0
- data/features/support/env.rb +1 -0
- data/features/support/world.rb +13 -0
- data/jenkins.sh +1 -1
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur/api.rb +12 -5
- data/lib/conjur/base.rb +38 -1
- data/lib/conjur/escape.rb +9 -3
- data/spec/lib/api_spec.rb +71 -24
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91bb6f8e907814c4dd388fef05384202a9e51a29
|
4
|
+
data.tar.gz: 4884d67d8ec68a02fdbabe297edeb1e8d95a6d88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 425b6e7ecbae6f9d79ee004a522ebe51d8ee091f452d3e6d30ff041a33dad04b15d33676b29179105e19f28b537be6325a0f951412a757236e07a1d03dee5954
|
7
|
+
data.tar.gz: a4fb4478261d5f9a677c19c2b8103e1f5a9a78015b36dcf17780a1c8288c43de3e3f854b26e1dde1f17c237bde971ac1d9cd1e2544b2fafd195ee10c798f9cdc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# v4.23.0
|
2
|
+
|
3
|
+
* Add `with_audit_roles` and `with_audit_resources` to `Conjur::API`
|
4
|
+
to add additional roles and resources to audit records generated by
|
5
|
+
requests
|
6
|
+
|
7
|
+
* Fix encoding of spaces in some urls.
|
8
|
+
|
1
9
|
# v4.22.1
|
2
10
|
|
3
11
|
* `bootstrap` creates host and webservice `conjur/expiration`.
|
data/conjur-api.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |gem|
|
|
28
28
|
gem.add_development_dependency 'rspec', '~> 3'
|
29
29
|
gem.add_development_dependency 'rspec-expectations', '~> 3.4'
|
30
30
|
gem.add_development_dependency 'webmock'
|
31
|
+
gem.add_development_dependency 'aruba', '~> 0.12.0'
|
31
32
|
gem.add_development_dependency 'cucumber'
|
32
33
|
gem.add_development_dependency 'conjur-cli'
|
33
34
|
gem.add_development_dependency 'conjur-debify'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: audit with additional resources
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given I create the variable "$ns_foo"
|
5
|
+
|
6
|
+
Scenario: with one additional resource
|
7
|
+
When I create an api with the additional audit resource "webservice:ws1"
|
8
|
+
And I check to see if I'm permitted to "read" variable "$ns_foo"
|
9
|
+
Then an audit event for variable "$ns_foo" with action "check" and resource "webservice:ws1" is generated
|
10
|
+
|
11
|
+
Scenario: with more than one additional resource
|
12
|
+
When I create an api with the additional audit resources "webservice:ws1, webservice:ws2"
|
13
|
+
And I check to see if I'm permitted to "read" variable "$ns_foo"
|
14
|
+
Then an audit event for variable "$ns_foo" with action "check" and resources "webservice:ws1, webservice:ws2" is generated
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: audit with additional resources
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given I create the variable "$ns_foo"
|
5
|
+
|
6
|
+
Scenario: with one additional resource
|
7
|
+
When I create an api with the additional audit role "user:auditor1"
|
8
|
+
And I check to see if I'm permitted to "read" variable "$ns_foo"
|
9
|
+
Then an audit event for variable "$ns_foo" with action "check" and role "user:auditor1" is generated
|
10
|
+
|
11
|
+
Scenario: with more than one additional resource
|
12
|
+
When I create an api with the additional audit roles "user:auditor2,group:auditors"
|
13
|
+
And I check to see if I'm permitted to "read" variable "$ns_foo"
|
14
|
+
Then an audit event for variable "$ns_foo" with action "check" and roles "user:auditor2,group:auditors" is generated
|
15
|
+
|
@@ -19,6 +19,39 @@ Then(/^expressions "([^"]*)" and "([^"]*)" are equal$/) do |code, test|
|
|
19
19
|
expect(eval(code)).to eq(eval(test))
|
20
20
|
end
|
21
21
|
|
22
|
+
Then(/^expression "(.*?)" is equal to$/) do |code, test|
|
23
|
+
step %Q{expressions "#{code}" and "#{test}" are equal}
|
24
|
+
end
|
25
|
+
|
22
26
|
Then(/^expression "([^"]*)" includes "([^"]*)"$/) do |code, test|
|
23
27
|
expect(eval(code)).to include(eval(test))
|
24
28
|
end
|
29
|
+
|
30
|
+
Then(/^I evaluate the expression "([^"]*)"$/) do |code|
|
31
|
+
eval(code)
|
32
|
+
end
|
33
|
+
|
34
|
+
Then(/^I evaluate the expression$/) do |code|
|
35
|
+
step %Q{I evaluate the expression "#{code}"}
|
36
|
+
end
|
37
|
+
|
38
|
+
Then(/^I create the variable "(.*?)"$/) do |var|
|
39
|
+
api.create_variable('text/plain', 'secret', :id => var)
|
40
|
+
end
|
41
|
+
|
42
|
+
Then(/^I create an api with the additional audit (role|resource)[s]* "(.*?)"$/) do |type, things|
|
43
|
+
@api = api.send("with_audit_#{type}s", things.split(','))
|
44
|
+
end
|
45
|
+
|
46
|
+
Then(/^I check to see if I'm permitted to "(.*?)" variable "(.*?)"$/) do |priv, var|
|
47
|
+
api.variable(var).resource.permitted?(priv)
|
48
|
+
end
|
49
|
+
|
50
|
+
Then(/^an audit event for variable "(.*?)" with action "(.*?)" and (role|resource)[s]* "(.*?)" is generated$/) do |var, action, type, things|
|
51
|
+
resource_ids = things.split(',').collect {|id| api.resource(id).resourceid }
|
52
|
+
event_found = api.audit_resource(api.resource("variable:#{var}")).any? do |e|
|
53
|
+
e['action'] == action &&
|
54
|
+
Set.new(e["#{type}s"]).superset?(Set.new(resource_ids))
|
55
|
+
end
|
56
|
+
expect(event_found).to be true
|
57
|
+
end
|
data/features/support/env.rb
CHANGED
data/jenkins.sh
CHANGED
data/lib/conjur-api/version.rb
CHANGED
data/lib/conjur/api.rb
CHANGED
@@ -97,11 +97,10 @@ class RestClient::Resource
|
|
97
97
|
# @return {Conjur::API} the new api
|
98
98
|
def conjur_api
|
99
99
|
api = Conjur::API.new_from_token token, remote_ip
|
100
|
-
if conjur_privilege
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
100
|
+
api = api.with_privilege(conjur_privilege) if conjur_privilege
|
101
|
+
api = api.with_audit_roles(audit_roles) if audit_roles
|
102
|
+
api = api.with_audit_resources(audit_resources) if audit_resources
|
103
|
+
api
|
105
104
|
end
|
106
105
|
|
107
106
|
# Get an authentication token from the clients Authorization header.
|
@@ -129,6 +128,14 @@ class RestClient::Resource
|
|
129
128
|
options[:headers][:x_conjur_privilege]
|
130
129
|
end
|
131
130
|
|
131
|
+
def audit_roles
|
132
|
+
options[:headers][:conjur_audit_roles].try { |r| Conjur::API.decode_audit_ids(r) }
|
133
|
+
end
|
134
|
+
|
135
|
+
def audit_resources
|
136
|
+
options[:headers][:conjur_audit_resources].try { |r| Conjur::API.decode_audit_ids(r) }
|
137
|
+
end
|
138
|
+
|
132
139
|
# The username this resource authenticates as.
|
133
140
|
#
|
134
141
|
# @return [String] the username
|
data/lib/conjur/base.rb
CHANGED
@@ -141,6 +141,15 @@ module Conjur
|
|
141
141
|
def new_from_token(token, remote_ip = nil)
|
142
142
|
self.new nil, nil, token, remote_ip
|
143
143
|
end
|
144
|
+
|
145
|
+
def encode_audit_ids(ids)
|
146
|
+
ids.collect{|id| CGI::escape(id)}.join('&')
|
147
|
+
end
|
148
|
+
|
149
|
+
def decode_audit_ids(ids)
|
150
|
+
ids.split('&').collect{|id| CGI::unescape(id)}
|
151
|
+
end
|
152
|
+
|
144
153
|
end
|
145
154
|
|
146
155
|
# Create a new instance from a username and api key or a token.
|
@@ -179,6 +188,16 @@ module Conjur
|
|
179
188
|
# The optional global privilege (e.g. 'elevate' or 'reveal') which should be attempted on the request.
|
180
189
|
attr_accessor :privilege
|
181
190
|
|
191
|
+
#@!attribute [rw] audit_roles
|
192
|
+
# An array of role ids that should be included in any audit
|
193
|
+
# records generated by requsts made by this instance of the api.
|
194
|
+
attr_accessor :audit_roles
|
195
|
+
|
196
|
+
#@!attribute [rw] audit_resources
|
197
|
+
# An array of resource ids that should be included in any audit
|
198
|
+
# records generated by requsts made by this instance of the api.
|
199
|
+
attr_accessor :audit_resources
|
200
|
+
|
182
201
|
# The name of the user as which this api instance is authenticated. This is available whether the api
|
183
202
|
# instance was created from credentials or an authentication token.
|
184
203
|
#
|
@@ -233,6 +252,8 @@ module Conjur
|
|
233
252
|
h[:authorization] = "Token token=\"#{Base64.strict_encode64 token.to_json}\""
|
234
253
|
h[:x_conjur_privilege] = @privilege if @privilege
|
235
254
|
h[:x_forwarded_for] = @remote_ip if @remote_ip
|
255
|
+
h[:conjur_audit_roles] = Conjur::API.encode_audit_ids(@audit_roles) if @audit_roles
|
256
|
+
h[:conjur_audit_resources] = Conjur::API.encode_audit_ids(@audit_resources) if @audit_resources
|
236
257
|
end
|
237
258
|
{ headers: headers, username: username }
|
238
259
|
end
|
@@ -245,7 +266,23 @@ module Conjur
|
|
245
266
|
api.privilege = privilege
|
246
267
|
end
|
247
268
|
end
|
248
|
-
|
269
|
+
|
270
|
+
def with_audit_roles role_ids
|
271
|
+
role_ids = Array(role_ids)
|
272
|
+
self.class.new(username, api_key, token, remote_ip).tap do |api|
|
273
|
+
# Ensure that all role ids are fully qualified
|
274
|
+
api.audit_roles = role_ids.collect { |id| api.role(id).roleid }
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def with_audit_resources resource_ids
|
279
|
+
resource_ids = Array(resource_ids)
|
280
|
+
self.class.new(username, api_key, token, remote_ip).tap do |api|
|
281
|
+
# Ensure that all resource ids are fully qualified
|
282
|
+
api.audit_resources = resource_ids.collect { |id| api.resource(id).resourceid }
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
249
286
|
private
|
250
287
|
|
251
288
|
def token_valid?
|
data/lib/conjur/escape.rb
CHANGED
@@ -31,13 +31,19 @@ module Conjur
|
|
31
31
|
# fully_escape 'foo/bar@baz'
|
32
32
|
# # => "foo%2Fbar%40baz"
|
33
33
|
#
|
34
|
+
# @example
|
35
|
+
# fully_escape 'test/Domain Controllers'
|
36
|
+
# # => "test%2FDomain%20Controllers"
|
37
|
+
#
|
34
38
|
# @param [String] str the string to escape
|
35
39
|
# @return [String] the escaped string
|
36
40
|
def fully_escape(str)
|
37
|
-
|
38
|
-
|
41
|
+
# CGI escape uses + for spaces, which our services don't support :-(
|
42
|
+
# We just gsub it.
|
43
|
+
CGI.escape(str.to_s).gsub('+', '%20')
|
39
44
|
end
|
40
45
|
|
46
|
+
|
41
47
|
# Escape a URI path component.
|
42
48
|
#
|
43
49
|
# This method simply calls {Conjur::Escape::ClassMethods#path_or_query_escape}.
|
@@ -121,4 +127,4 @@ module Conjur
|
|
121
127
|
self.class.query_escape str
|
122
128
|
end
|
123
129
|
end
|
124
|
-
end
|
130
|
+
end
|
data/spec/lib/api_spec.rb
CHANGED
@@ -260,6 +260,7 @@ describe Conjur::API do
|
|
260
260
|
end
|
261
261
|
end
|
262
262
|
end
|
263
|
+
|
263
264
|
|
264
265
|
context "from api key", logged_in: true do
|
265
266
|
let(:api_key) { "theapikey" }
|
@@ -299,38 +300,67 @@ describe Conjur::API do
|
|
299
300
|
end
|
300
301
|
|
301
302
|
context "from logged-in RestClient::Resource" do
|
303
|
+
let (:authz_header) { %Q{Token token="#{token_encoded}"} }
|
304
|
+
let (:priv_header) { nil }
|
305
|
+
let (:forwarded_for_header) { nil }
|
306
|
+
let (:audit_roles_header) { nil }
|
307
|
+
let (:audit_resources_header) { nil }
|
308
|
+
let (:username) { 'bob' }
|
309
|
+
subject { resource.conjur_api }
|
310
|
+
|
311
|
+
shared_examples "it can clone itself" do
|
312
|
+
it "has the authz header" do
|
313
|
+
expect(subject.credentials[:headers][:authorization]).to eq(authz_header)
|
314
|
+
end
|
315
|
+
it "has the conjur privilege header" do
|
316
|
+
expect(subject.credentials[:headers][:x_conjur_privilege]).to eq(priv_header)
|
317
|
+
end
|
318
|
+
it "has the forwarded for header" do
|
319
|
+
expect(subject.credentials[:headers][:x_forwarded_for]).to eq(forwarded_for_header)
|
320
|
+
end
|
321
|
+
it "has the audit_roles header" do
|
322
|
+
expect(subject.credentials[:headers][:conjur_audit_roles]).to eq(audit_roles_header)
|
323
|
+
end
|
324
|
+
it "has the audit_resources header" do
|
325
|
+
expect(subject.credentials[:headers][:conjur_audit_resources]).to eq(audit_resources_header)
|
326
|
+
end
|
327
|
+
it "has the username" do
|
328
|
+
expect(subject.credentials[:username]).to eq(username)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
302
332
|
let(:token_encoded) { Base64.strict_encode64(token.to_json) }
|
303
|
-
let(:
|
333
|
+
let(:base_headers) { { authorization: authz_header } }
|
334
|
+
let(:headers) { base_headers }
|
304
335
|
let(:resource) { RestClient::Resource.new("http://example.com", { headers: headers })}
|
305
|
-
|
306
|
-
|
307
|
-
expect(api.credentials[:headers][:authorization]).to eq("Token token=\"#{token_encoded}\"")
|
308
|
-
expect(api.credentials[:headers][:x_conjur_privilege]).to be_nil
|
309
|
-
expect(api.credentials[:headers][:x_forwarded_for]).to be_nil
|
310
|
-
expect(api.credentials[:username]).to eq("bob")
|
336
|
+
context 'basic functioning' do
|
337
|
+
it_behaves_like 'it can clone itself'
|
311
338
|
end
|
312
339
|
|
313
340
|
context "privileged" do
|
314
|
-
let(:
|
315
|
-
|
316
|
-
|
317
|
-
expect(api.credentials[:headers][:authorization]).to eq("Token token=\"#{token_encoded}\"")
|
318
|
-
expect(api.credentials[:headers][:x_conjur_privilege]).to eq("elevate")
|
319
|
-
expect(api.credentials[:headers][:x_forwarded_for]).to be_nil
|
320
|
-
expect(api.credentials[:username]).to eq("bob")
|
321
|
-
end
|
341
|
+
let(:priv_header) { 'elevate' }
|
342
|
+
let(:headers) { base_headers.merge(x_conjur_privilege: priv_header) }
|
343
|
+
it_behaves_like "it can clone itself"
|
322
344
|
end
|
323
345
|
|
324
|
-
context "
|
325
|
-
let(:
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
346
|
+
context "forwarded for" do
|
347
|
+
let(:forwarded_for_header) { "66.0.0.1" }
|
348
|
+
let(:headers) { base_headers.merge(x_forwarded_for: forwarded_for_header) }
|
349
|
+
it_behaves_like 'it can clone itself'
|
350
|
+
end
|
351
|
+
|
352
|
+
context "audit roles" do
|
353
|
+
let(:audit_roles_header) { Conjur::API.encode_audit_ids(['account:kind:role1', 'account:kind:role2']) }
|
354
|
+
let(:headers) { base_headers.merge(:conjur_audit_roles => audit_roles_header) }
|
355
|
+
it_behaves_like 'it can clone itself'
|
356
|
+
end
|
357
|
+
|
358
|
+
context "audit resources" do
|
359
|
+
let(:audit_resources_header) { Conjur::API.encode_audit_ids(['account:kind:resource1', 'account:kind:resource2']) }
|
360
|
+
let(:headers) { base_headers.merge(:conjur_audit_resources => audit_resources_header) }
|
361
|
+
it_behaves_like 'it can clone itself'
|
333
362
|
end
|
363
|
+
|
334
364
|
end
|
335
365
|
end
|
336
366
|
|
@@ -360,4 +390,21 @@ describe Conjur::API do
|
|
360
390
|
end
|
361
391
|
end
|
362
392
|
end
|
393
|
+
|
394
|
+
describe 'url escapes' do
|
395
|
+
let(:urls){[
|
396
|
+
'foo/bar@baz',
|
397
|
+
'/test/some group with spaces'
|
398
|
+
]}
|
399
|
+
|
400
|
+
describe '#fully_escape' do
|
401
|
+
let(:expected){[
|
402
|
+
'foo%2Fbar%40baz',
|
403
|
+
'%2Ftest%2Fsome%20group%20with%20spaces'
|
404
|
+
]}
|
405
|
+
it 'escapes the urls correctly' do
|
406
|
+
expect(urls.map{|u| Conjur::API.fully_escape u}).to eq(expected)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
363
410
|
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.
|
4
|
+
version: 4.23.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-
|
12
|
+
date: 2016-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -129,6 +129,20 @@ dependencies:
|
|
129
129
|
- - '>='
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: aruba
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.12.0
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.12.0
|
132
146
|
- !ruby/object:Gem::Dependency
|
133
147
|
name: cucumber
|
134
148
|
requirement: !ruby/object:Gem::Requirement
|
@@ -323,9 +337,13 @@ files:
|
|
323
337
|
- Rakefile
|
324
338
|
- ci/test.sh
|
325
339
|
- conjur-api.gemspec
|
340
|
+
- features/audit_resources.feature
|
341
|
+
- features/audit_roles.feature
|
326
342
|
- features/bootstrap.feature
|
327
343
|
- features/step_definitions/api_steps.rb
|
344
|
+
- features/step_definitions/cli_steps.rb
|
328
345
|
- features/support/env.rb
|
346
|
+
- features/support/world.rb
|
329
347
|
- jenkins.sh
|
330
348
|
- lib/conjur-api.rb
|
331
349
|
- lib/conjur-api/version.rb
|
@@ -459,9 +477,13 @@ signing_key:
|
|
459
477
|
specification_version: 4
|
460
478
|
summary: Conjur API
|
461
479
|
test_files:
|
480
|
+
- features/audit_resources.feature
|
481
|
+
- features/audit_roles.feature
|
462
482
|
- features/bootstrap.feature
|
463
483
|
- features/step_definitions/api_steps.rb
|
484
|
+
- features/step_definitions/cli_steps.rb
|
464
485
|
- features/support/env.rb
|
486
|
+
- features/support/world.rb
|
465
487
|
- spec/api/authn_spec.rb
|
466
488
|
- spec/api/graph_spec.rb
|
467
489
|
- spec/api/groups_spec.rb
|