conjur-api 4.6.1 → 4.7.1

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.
@@ -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