conjur-api 4.6.1 → 4.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur/annotations.rb +78 -0
- data/lib/conjur/api.rb +13 -0
- data/lib/conjur/api/resources.rb +4 -1
- data/lib/conjur/resource.rb +16 -2
- data/spec/api/resources_spec.rb +2 -2
- data/spec/lib/annotations_spec.rb +102 -0
- data/spec/lib/api_spec.rb +9 -0
- data/spec/lib/resource_spec.rb +10 -0
- data/spec/spec_helper.rb +1 -0
- metadata +6 -3
data/lib/conjur-api/version.rb
CHANGED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Conjur
|
2
|
+
# An Annotations instance acts like a Hash: you can fetch an annotation
|
3
|
+
# with '[]' and update with '[]=', 'each' it, and 'merge!' to do bulk updates
|
4
|
+
# (although merge! is not more efficient than setting each pair with '[]=').
|
5
|
+
class Annotations
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
def initialize resource
|
10
|
+
@resource = resource
|
11
|
+
end
|
12
|
+
|
13
|
+
# Get the value of the annotation with the given name
|
14
|
+
# @param [String,Symbol] name the annotation name, indifferent to whether it's
|
15
|
+
# a String or Symbol.
|
16
|
+
def [] name
|
17
|
+
annotations_hash[name.to_sym]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set an annotation value
|
21
|
+
# @param [String, Symbol] name the annotation name
|
22
|
+
# @param [String] value the annotation value
|
23
|
+
def []= name, value
|
24
|
+
update_annotation name, value
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
# Enumerate all annotations, yielding key,value pairs.
|
29
|
+
# @return [Conjur::Annotations] self
|
30
|
+
def each &blk
|
31
|
+
annotations_hash.each &blk
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set annotations from key,value pairs in +hash+
|
36
|
+
# @param [#each] hash
|
37
|
+
# @return [Conjur::Annotations] self
|
38
|
+
def merge! hash
|
39
|
+
hash.each{|k,v| self[k] = v }
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return a proper hash containing a +copy+ of ourself. Note that
|
44
|
+
# updates to this hash have no effect on the actual annotations.
|
45
|
+
# @return [Hash]
|
46
|
+
def to_h
|
47
|
+
annotations_hash.dup
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
annotations_hash.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
"<Annotations for #{@resource.resourceid}: #{to_s}>"
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def update_annotation name, value
|
61
|
+
@annotations_hash = nil
|
62
|
+
path = [@resource.account,'annotations', @resource.kind, @resource.identifier].join '/'
|
63
|
+
RestClient::Resource.new(Conjur::Authz::API.host, @resource.options)[path].put name: name, value: value
|
64
|
+
end
|
65
|
+
|
66
|
+
def annotations_hash
|
67
|
+
@annotations_hash ||= fetch_annotations
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch_annotations
|
71
|
+
{}.tap do |hash|
|
72
|
+
JSON.parse(@resource.get)['annotations'].each do |annotation|
|
73
|
+
hash[annotation['name'].to_sym] = annotation['value']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/conjur/api.rb
CHANGED
@@ -51,6 +51,19 @@ class RestClient::Resource
|
|
51
51
|
{}
|
52
52
|
end
|
53
53
|
|
54
|
+
def conjur_api
|
55
|
+
Conjur::API.new_from_token token
|
56
|
+
end
|
57
|
+
|
58
|
+
def token
|
59
|
+
authorization = options[:headers][:authorization]
|
60
|
+
if authorization && authorization.to_s[/^Token token="(.*)"/]
|
61
|
+
JSON.parse(Base64.decode64($1))
|
62
|
+
else
|
63
|
+
raise AuthorizationError.new("Authorization missing")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
54
67
|
def username
|
55
68
|
options[:user] || options[:username]
|
56
69
|
end
|
data/lib/conjur/api/resources.rb
CHANGED
@@ -35,7 +35,10 @@ module Conjur
|
|
35
35
|
# Return all visible resources.
|
36
36
|
# In opts you should pass an account to filter by, and optionally a kind.
|
37
37
|
def resources opts = {}
|
38
|
-
|
38
|
+
opts = { host: Conjur::Authz::API.host, credentials: credentials }.merge opts
|
39
|
+
opts[:account] ||= Conjur.account
|
40
|
+
|
41
|
+
Resource.all(opts).map do |result|
|
39
42
|
resource(result['id']).tap do |r|
|
40
43
|
r.attributes = result
|
41
44
|
end
|
data/lib/conjur/resource.rb
CHANGED
@@ -18,6 +18,8 @@
|
|
18
18
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
|
+
require 'conjur/annotations'
|
22
|
+
|
21
23
|
module Conjur
|
22
24
|
class Resource < RestClient::Resource
|
23
25
|
include Exists
|
@@ -106,6 +108,12 @@ module Conjur
|
|
106
108
|
rescue RestClient::ResourceNotFound
|
107
109
|
false
|
108
110
|
end
|
111
|
+
|
112
|
+
# Return a Conjur::Annotations instance to read and manipulate our annotations.
|
113
|
+
def annotations
|
114
|
+
@annotations ||= Conjur::Annotations.new(self)
|
115
|
+
end
|
116
|
+
alias tags annotations
|
109
117
|
|
110
118
|
# Returns all resources (optionally qualified by kind)
|
111
119
|
# visible to the user with given credentials.
|
@@ -113,16 +121,22 @@ module Conjur
|
|
113
121
|
# - host - authz url,
|
114
122
|
# - credentials,
|
115
123
|
# - account,
|
116
|
-
# - kind (optional)
|
124
|
+
# - kind (optional),
|
125
|
+
# - search (optional),
|
126
|
+
# - limit (optional),
|
127
|
+
# - offset (optional).
|
117
128
|
def self.all opts = {}
|
118
129
|
host, credentials, account, kind = opts.values_at(*[:host, :credentials, :account, :kind])
|
119
130
|
fail ArgumentError, "host and account are required" unless [host, account].all?
|
120
131
|
|
121
132
|
credentials ||= {}
|
122
133
|
|
123
|
-
path = "#{account}/resources"
|
134
|
+
path = "#{account}/resources"
|
124
135
|
path += "/#{kind}" if kind
|
136
|
+
query = opts.slice(:acting_as, :limit, :offset, :search)
|
137
|
+
path += "?#{query.to_query}" unless query.empty?
|
125
138
|
resource = RestClient::Resource.new(host, credentials)[path]
|
139
|
+
|
126
140
|
JSON.parse resource.get
|
127
141
|
end
|
128
142
|
|
data/spec/api/resources_spec.rb
CHANGED
@@ -33,13 +33,13 @@ describe Conjur::API, api: :dummy do
|
|
33
33
|
}
|
34
34
|
it "lists all resources" do
|
35
35
|
expect(Conjur::Resource).to receive(:all)
|
36
|
-
.with(host: authz_host, credentials: api.credentials).and_return(resources)
|
36
|
+
.with(host: authz_host, account: account, credentials: api.credentials).and_return(resources)
|
37
37
|
|
38
38
|
expect(api.resources.map(&:url)).to eql(ids.map { |id| api.resource(id).url })
|
39
39
|
end
|
40
40
|
it "can filter by kind" do
|
41
41
|
expect(Conjur::Resource).to receive(:all)
|
42
|
-
.with(host: authz_host, credentials: api.credentials, kind: :chunky).and_return(resources)
|
42
|
+
.with(host: authz_host, account: account, credentials: api.credentials, kind: :chunky).and_return(resources)
|
43
43
|
|
44
44
|
expect(api.resources(kind: :chunky).map(&:url)).to eql(ids.map { |id| api.resource(id).url })
|
45
45
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Conjur::Annotations do
|
4
|
+
let(:identifier){ 'the-resource-id' }
|
5
|
+
let(:kind){ 'some-kind' }
|
6
|
+
let(:account){ 'the-account' }
|
7
|
+
let(:resourceid){ [account, kind, identifier].join ':'}
|
8
|
+
let(:options){ { } }
|
9
|
+
let(:raw_annotations){ [{'name' => 'name', 'value' => 'bar'},
|
10
|
+
{'name' => 'comment', 'value' => 'some comment'}] }
|
11
|
+
let(:resource_hash){ { 'annotations' => raw_annotations } }
|
12
|
+
let(:resource){
|
13
|
+
double('resource', account: account,
|
14
|
+
kind: kind, identifier: identifier,
|
15
|
+
options: options, resourceid: resourceid,
|
16
|
+
get: resource_hash.to_json)
|
17
|
+
}
|
18
|
+
let(:annotations){ Conjur::Annotations.new(resource) }
|
19
|
+
|
20
|
+
subject{ annotations }
|
21
|
+
|
22
|
+
|
23
|
+
let(:url){ "#{Conjur::Authz::API.host}/#{account}/annotations/#{kind}/#{identifier}" }
|
24
|
+
|
25
|
+
def expect_put_request url, payload
|
26
|
+
RestClient::Request.should_receive(:execute).with(
|
27
|
+
method: :put,
|
28
|
+
headers: {},
|
29
|
+
url: url,
|
30
|
+
payload: payload
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '[]' do
|
35
|
+
it "returns annotations" do
|
36
|
+
subject[:name].should == 'bar'
|
37
|
+
subject[:comment].should == 'some comment'
|
38
|
+
subject['comment'].should == subject[:comment]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "caches the get result" do
|
42
|
+
resource.should_receive(:get).exactly(1).times.and_return(resource_hash.to_json)
|
43
|
+
subject[:name]
|
44
|
+
subject[:name]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#each' do
|
49
|
+
it "yields each annotation pair" do
|
50
|
+
pairs = []
|
51
|
+
subject.each{|k,v| pairs << [k,v]}
|
52
|
+
pairs.should == [[:name, 'bar'], [:comment, 'some comment']]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "is Enumerable" do
|
57
|
+
subject.should be_a(Enumerable)
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#to_h' do
|
61
|
+
it "returns the correct hash" do
|
62
|
+
subject.to_h.should == {name: 'bar', comment: 'some comment'}
|
63
|
+
end
|
64
|
+
it "does not propagate modifications to the returned hash" do
|
65
|
+
RestClient::Request.should_not_receive(:execute)
|
66
|
+
subject.to_h[:name] = 'new name'
|
67
|
+
subject[:name].should == subject.to_h[:name]
|
68
|
+
subject[:name].should == "bar"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#merge!" do
|
73
|
+
let(:hash){ {blah: 'blahbah', zelda: 'link'} }
|
74
|
+
|
75
|
+
it "makes a put request for each pair" do
|
76
|
+
hash.each do |k,v|
|
77
|
+
expect_put_request(url, name: k, value: v)
|
78
|
+
end
|
79
|
+
subject.merge! hash
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '[]=' do
|
84
|
+
|
85
|
+
it "makes a put request" do
|
86
|
+
expect_put_request url, name: :blah, value: 'boo'
|
87
|
+
subject[:blah] = 'boo'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "forces a fresh request for the annotations" do
|
91
|
+
expect_put_request(url, name: :foo, value: 'bar')
|
92
|
+
resource.should_receive(:get).exactly(2).times.and_return(resource_hash.to_json)
|
93
|
+
# One get request
|
94
|
+
subject[:name].should == 'bar'
|
95
|
+
# Update
|
96
|
+
subject[:foo] = 'bar'
|
97
|
+
# Second get request
|
98
|
+
subject[:name].should == 'bar'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/spec/lib/api_spec.rb
CHANGED
@@ -214,6 +214,15 @@ describe Conjur::API do
|
|
214
214
|
api.credentials.should == { headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login }
|
215
215
|
end
|
216
216
|
end
|
217
|
+
context "from logged-in RestClient::Resource" do
|
218
|
+
let(:token_encoded) { Base64.strict_encode64(token.to_json) }
|
219
|
+
let(:resource) { RestClient::Resource.new("http://example.com", { headers: { authorization: "Token token=\"#{token_encoded}\"" } })}
|
220
|
+
it "can construct a new API instance" do
|
221
|
+
api = resource.conjur_api
|
222
|
+
api.credentials[:headers][:authorization].should == "Token token=\"#{token_encoded}\""
|
223
|
+
api.credentials[:username].should == "bob"
|
224
|
+
end
|
225
|
+
end
|
217
226
|
end
|
218
227
|
|
219
228
|
describe "#role_from_username", logged_in: true do
|
data/spec/lib/resource_spec.rb
CHANGED
@@ -148,6 +148,16 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
|
|
148
148
|
expect(Conjur::Resource.all host: authz_host, account: account, kind: :chunky)
|
149
149
|
.to eql(%w(foo bar))
|
150
150
|
end
|
151
|
+
|
152
|
+
it "passes search, limit, and offset params" do
|
153
|
+
RestClient::Request.should_receive(:execute).with(
|
154
|
+
method: :get,
|
155
|
+
# Note that to_query sorts the keys
|
156
|
+
url: "http://authz.example.com/the-account/resources?limit=5&offset=6&search=something",
|
157
|
+
headers: {}
|
158
|
+
).and_return '["foo", "bar"]'
|
159
|
+
Conjur::Resource.all(host: authz_host, account: account, search: 'something', limit:5, offset:6).should == %w(foo bar)
|
160
|
+
end
|
151
161
|
|
152
162
|
it "uses the given authz url" do
|
153
163
|
RestClient::Request.should_receive(:execute).with(
|
data/spec/spec_helper.rb
CHANGED
@@ -105,6 +105,7 @@ shared_context api: :dummy do
|
|
105
105
|
Conjur::Core::API.stub host: core_host
|
106
106
|
Conjur::Core::API.stub conjur_account: account
|
107
107
|
Conjur::Audit::API.stub host:audit_host
|
108
|
+
Conjur.configuration.set :account, account
|
108
109
|
api.stub credentials: credentials
|
109
110
|
end
|
110
111
|
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.7.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-03-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rest-client
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/conjur/acts_as_resource.rb
|
215
215
|
- lib/conjur/acts_as_role.rb
|
216
216
|
- lib/conjur/acts_as_user.rb
|
217
|
+
- lib/conjur/annotations.rb
|
217
218
|
- lib/conjur/api.rb
|
218
219
|
- lib/conjur/api/audit.rb
|
219
220
|
- lib/conjur/api/authn.rb
|
@@ -262,6 +263,7 @@ files:
|
|
262
263
|
- spec/api/users_spec.rb
|
263
264
|
- spec/api/variables_spec.rb
|
264
265
|
- spec/cas_rest_client.rb
|
266
|
+
- spec/lib/annotations_spec.rb
|
265
267
|
- spec/lib/api_spec.rb
|
266
268
|
- spec/lib/asset_spec.rb
|
267
269
|
- spec/lib/audit_spec.rb
|
@@ -304,7 +306,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
304
306
|
version: '0'
|
305
307
|
segments:
|
306
308
|
- 0
|
307
|
-
hash:
|
309
|
+
hash: 1666526476905320839
|
308
310
|
requirements: []
|
309
311
|
rubyforge_project:
|
310
312
|
rubygems_version: 1.8.25
|
@@ -325,6 +327,7 @@ test_files:
|
|
325
327
|
- spec/api/users_spec.rb
|
326
328
|
- spec/api/variables_spec.rb
|
327
329
|
- spec/cas_rest_client.rb
|
330
|
+
- spec/lib/annotations_spec.rb
|
328
331
|
- spec/lib/api_spec.rb
|
329
332
|
- spec/lib/asset_spec.rb
|
330
333
|
- spec/lib/audit_spec.rb
|