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.
@@ -20,6 +20,6 @@
20
20
  #
21
21
  module Conjur
22
22
  class API
23
- VERSION = "4.6.1"
23
+ VERSION = "4.7.1"
24
24
  end
25
25
  end
@@ -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
@@ -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
@@ -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
- Resource.all({ host: Conjur::Authz::API.host, credentials: credentials }.merge opts).map do |result|
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
@@ -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
 
@@ -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
@@ -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
@@ -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(
@@ -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.6.1
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-02-28 00:00:00.000000000 Z
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: -564295811495519523
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