conjur-api 4.29.2 → 4.30.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3501dba172c6c99ebdc99ad7fef80634e39cf2c3
4
- data.tar.gz: 620f5813442acedbcd510ac2756bd5a6e8d700a8
3
+ metadata.gz: bac541f4dc5eb324e42a7dce3dc436312b98ef18
4
+ data.tar.gz: 184c9e5ec253703bd0918b31145a4fa1c26d0d0f
5
5
  SHA512:
6
- metadata.gz: a090764853263323c9d21afcde4069a9481aabb1dcd668c2407f0a8e349cf50439e6dd80924b51a86e35cf6dbc951ee93baa9e03524164452b95600955914c28
7
- data.tar.gz: a6bb48a6b4fe8b4a505a847f7915904d00ddcd4e86a13e7b5dcd059ae269ef5dd82d565bb20431babc2d48992ded7227e85bb9381dbb355473edc5283340da01
6
+ metadata.gz: 25fc29905946cadcd53c6828eadc568cde3f05790a78987093d3219396130ab36cfc920c723e455126f0d573c1be85b7ff5b1c057facc50c9272fa6e7d7cf39b
7
+ data.tar.gz: ca362cd00b847a5269fe723b1c39eaee9798531d73e55b9e897f369a85165c98f8be2ebf3cac43befbdb1f7d4b392b26c51aa21328ec6e19d8a0ac713213ce31
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # v4.30.0
2
+
3
+ The following enhancements require Conjur server 4.9.1.0 or later:
4
+
5
+ * Supports filter and pagination of role-listing methods.
6
+ * Supports non-recursive retrieval of role memberships.
7
+ * Supports the +role+ field on `Conjur::RoleGrant`.
8
+
9
+ On older server versions, the new options will be ignored by the server.
10
+
1
11
  # v4.29.2
2
12
 
3
13
  * `Conjur::API#resources` now supports `:owner` to retrieve all resources owned (directly or indirectly) by the indicated role. This capability has always been provided by the service, but was not exposed by the Ruby API.
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Conjur
21
21
  class API
22
- VERSION = "4.29.2"
22
+ VERSION = "4.30.0"
23
23
  end
24
24
  end
@@ -141,9 +141,14 @@ module Conjur
141
141
  opts = { host: Conjur::Authz::API.host, credentials: credentials }.merge opts
142
142
  opts[:account] ||= Conjur.account
143
143
 
144
- Resource.all(opts).map do |result|
145
- resource(result['id']).tap do |r|
146
- r.attributes = result
144
+ result = Resource.all(opts)
145
+ if result.is_a?(Numeric)
146
+ result
147
+ else
148
+ result.map do |result|
149
+ resource(result['id']).tap do |r|
150
+ r.attributes = result
151
+ end
147
152
  end
148
153
  end
149
154
  end
data/lib/conjur/base.rb CHANGED
@@ -22,6 +22,7 @@ require 'rest-client'
22
22
  require 'json'
23
23
  require 'base64'
24
24
 
25
+ require 'conjur/query_string'
25
26
  require 'conjur/exists'
26
27
  require 'conjur/has_attributes'
27
28
  require 'conjur/has_owner'
@@ -0,0 +1,11 @@
1
+ module QueryString
2
+ protected
3
+
4
+ def options_querystring options
5
+ if options.empty?
6
+ ""
7
+ else
8
+ "?#{options.to_query}"
9
+ end
10
+ end
11
+ end
@@ -33,6 +33,8 @@ module Conjur
33
33
  include HasAttributes
34
34
  include PathBased
35
35
  include Exists
36
+ include QueryString
37
+ extend QueryString
36
38
 
37
39
  alias resource_kind kind
38
40
 
@@ -89,10 +91,15 @@ module Conjur
89
91
  self.put(options)
90
92
  end
91
93
 
92
- # Lists roles that have a specified permission on the resource.
94
+ # Lists roles that have a specified permission on the resource.
93
95
  #
94
96
  # This will return only roles of which api.current_user is a member.
95
97
  #
98
+ # Options:
99
+ #
100
+ # * **offset** Zero-based offset into the result set.
101
+ # * **limit** Total number of records returned.
102
+ #
96
103
  # @example
97
104
  # resource = api.resource 'conjur:variable:example'
98
105
  # resource.permitted_roles 'execute' # => ['conjur:user:admin']
@@ -100,10 +107,16 @@ module Conjur
100
107
  # resource.permitted_roles 'execute' # => ['conjur:user:admin', 'conjur:user:jon']
101
108
  #
102
109
  # @param permission [String] the permission
103
- # @param options [Hash, nil] extra options to pass to RestClient::Resource#get
104
- # @return [Array<String>] the ids of roles that have `permission` on this resource.
110
+ # @param options [Hash, nil] extra parameters to pass to the webservice method.
111
+ # @return [Array<String>] the ids of roles that have `permission` on this resource, sorted
112
+ # alphabetically.
105
113
  def permitted_roles(permission, options = {})
106
- JSON.parse RestClient::Resource.new(Conjur::Authz::API.host, self.options)["#{account}/roles/allowed_to/#{permission}/#{path_escape kind}/#{path_escape identifier}"].get(options)
114
+ result = JSON.parse RestClient::Resource.new(Conjur::Authz::API.host, self.options)["#{account}/roles/allowed_to/#{permission}/#{path_escape kind}/#{path_escape identifier}#{options_querystring options}"].get
115
+ if result.is_a?(Hash) && ( count = result['count'] )
116
+ count
117
+ else
118
+ result
119
+ end
107
120
  end
108
121
 
109
122
  # Changes the owner of a resource. You must be the owner of the resource
@@ -275,19 +288,22 @@ module Conjur
275
288
  # - search (optional),
276
289
  # - limit (optional),
277
290
  # - offset (optional).
278
- def self.all opts = {}
279
- host, credentials, account, kind = opts.values_at(*[:host, :credentials, :account, :kind])
291
+ def self.all options = {}
292
+ host, credentials, account, kind = options.values_at(*[:host, :credentials, :account, :kind])
280
293
  fail ArgumentError, "host and account are required" unless [host, account].all?
294
+ %w(host credentials account kind).each do |name|
295
+ options.delete(name.to_sym)
296
+ end
281
297
 
282
298
  credentials ||= {}
283
299
 
284
300
  path = "#{account}/resources"
285
301
  path += "/#{kind}" if kind
286
- query = opts.slice(:owner, :acting_as, :limit, :offset, :search, :has_annotation)
287
- path += "?#{query.to_query}" unless query.empty?
288
- resource = RestClient::Resource.new(host, credentials)[path]
289
-
290
- JSON.parse resource.get
302
+
303
+ result = JSON.parse(RestClient::Resource.new(host, credentials)[path][options_querystring options].get)
304
+
305
+ result = result['count'] if result.is_a?(Hash)
306
+ result
291
307
  end
292
308
 
293
309
  protected
data/lib/conjur/role.rb CHANGED
@@ -33,6 +33,8 @@ module Conjur
33
33
  class Role < RestClient::Resource
34
34
  include Exists
35
35
  include PathBased
36
+ include QueryString
37
+
36
38
 
37
39
  # The *unqualified* identifier for this role.
38
40
  #
@@ -72,7 +74,7 @@ module Conjur
72
74
  self.put(options)
73
75
  end
74
76
 
75
- # Find all roles of which this role is a member. This relationship is recursively expanded,
77
+ # Find all roles of which this role is a member. By default, role relationships are recursively expanded,
76
78
  # so if `a` is a member of `b`, and `b` is a member of `c`, `a.all` will include `c`.
77
79
  #
78
80
  # ### Permissions
@@ -81,6 +83,10 @@ module Conjur
81
83
  # You can restrict the roles returned to one or more role ids. This feature is mainly useful
82
84
  # for checking whether this role is a member of any of a set of roles.
83
85
  #
86
+ # ### Options
87
+ #
88
+ # * **recursive** Defaults to +true+, performs recursive expansion of the memberships.
89
+ #
84
90
  # @example Show all roles of which `"conjur:group:pubkeys-1.0/key-managers"` is a member
85
91
  # # Add alice to the group, so we see something interesting
86
92
  # key_managers = api.group('pubkeys-1.0/key-managers')
@@ -94,18 +100,30 @@ module Conjur
94
100
  # is_member = api.role('conjur:user:alice').all(filter: ['conjur:group:developers', 'conjur:group:ops']).any?
95
101
  #
96
102
  # @param [Hash] options options for the request
97
- # @option options [String, #roleid, Array<String, #roleid>] :filter only return roles in this list
103
+ # @param options [Hash, nil] :filter only return roles in this list. Also, extra parameters to pass to the webservice method.
98
104
  # @return [Array<Conjur::Role>] Roles of which this role is a member
99
105
  def all(options = {})
100
- query_string = "?all"
101
-
106
+ request = if options.delete(:recursive) == false
107
+ options["memberships"] = true
108
+ else
109
+ options["all"] = true
110
+ end
102
111
  if filter = options.delete(:filter)
103
112
  filter = [filter] unless filter.is_a?(Array)
104
- filter.map!{ |obj| cast(obj, :roleid) }
105
- (query_string << "&" << filter.to_query("filter")) unless filter.empty?
113
+ options["filter"] = filter.map{ |obj| cast(obj, :roleid) }
106
114
  end
107
- JSON.parse(self[query_string].get(options)).collect do |id|
108
- Role.new(Conjur::Authz::API.host, self.options)[Conjur::API.parse_role_id(id).join('/')]
115
+
116
+ result = JSON.parse(self[options_querystring options].get)
117
+ if result.is_a?(Hash) && ( count = result['count'] )
118
+ count
119
+ else
120
+ result.collect do |item|
121
+ if item.is_a?(String)
122
+ Role.new(Conjur::Authz::API.host, self.options)[Conjur::API.parse_role_id(item).join('/')]
123
+ else
124
+ RoleGrant.parse_from_json(item, self.options)
125
+ end
126
+ end
109
127
  end
110
128
  end
111
129
 
@@ -300,17 +318,23 @@ module Conjur
300
318
  end
301
319
 
302
320
 
303
- # Fetch the members of this role. The results are *not* recursively expanded (in contrast to {#memberships}).
321
+ # Fetch the direct members of this role. The results are *not* recursively expanded).
304
322
  #
305
323
  # ### Permissions
306
324
  # You must be a member of the role to call this method.
307
325
  #
308
- # @param [Hash] options unused and included only for backwards compatibility
326
+ # @param options [Hash, nil] extra parameters to pass to the webservice method.
309
327
  # @return [Array<Conjur::RoleGrant>] the role memberships
310
328
  # @raise [RestClient::Forbidden] if you don't have permission to perform this operation
311
- def members
312
- JSON.parse(self["?members"].get(options)).collect do |json|
313
- RoleGrant.parse_from_json(json, self.options)
329
+ def members options = {}
330
+ options["members"] = true
331
+ result = JSON.parse(self[options_querystring options].get)
332
+ if result.is_a?(Hash) && ( count = result['count'] )
333
+ count
334
+ else
335
+ result.collect do |json|
336
+ RoleGrant.parse_from_json(json, self.options)
337
+ end
314
338
  end
315
339
  end
316
340
  end
@@ -27,23 +27,24 @@ module Conjur
27
27
  # admin_role.members.map{|grant| grant.member}.include? alice # => true
28
28
  #
29
29
  class RoleGrant
30
-
30
+ # The role which was granted.
31
+ # @return [Conjur::Role]
32
+ attr_reader :role
31
33
 
32
34
  # The member role in the relationship
33
- # @return [Conjur::Role] the member
35
+ # @return [Conjur::Role]
34
36
  attr_reader :member
35
37
 
36
38
  # The role that created this grant.
37
39
  #
38
- # @return [Conjur::Role] the role that created the grant
40
+ # @return [Conjur::Role]
39
41
  attr_reader :grantor
40
42
 
41
43
  # When true, the role {#member} is allowed to give this grant to other roles
42
44
  #
43
- # @return [Boolean] whether the role can grant the role to others
45
+ # @return [Boolean]
44
46
  attr_reader :admin_option
45
47
 
46
-
47
48
  # @api private
48
49
  #
49
50
  # Create a new RoleGrant instance.
@@ -51,12 +52,24 @@ module Conjur
51
52
  # @param [Conjur::Role] member the member to which the role was granted
52
53
  # @param [Conjur::Role] grantor the role that created this grant
53
54
  # @param [Boolean] admin_option whether `member` can give the grant to other roles
54
- def initialize member, grantor, admin_option
55
+ def initialize role, member, grantor, admin_option
56
+ @role = role
55
57
  @member = member
56
58
  @grantor = grantor
57
59
  @admin_option = admin_option
58
60
  end
59
61
 
62
+ # Representation of the role grant as a hash.
63
+ def to_h
64
+ {
65
+ member: member.roleid,
66
+ grantor: grantor.roleid,
67
+ admin_option: admin_option
68
+ }.tap do |h|
69
+ h[:role] = role.roleid if role
70
+ end
71
+ end
72
+
60
73
  #@!attribute member
61
74
  # The member thing
62
75
  # @return [Conjur::Role] a ret?
@@ -70,9 +83,15 @@ module Conjur
70
83
  # @param [Hash] credentials the credentials used to create APIs for the member and grantor role objects
71
84
  # @return [Conjur::RoleGrant]
72
85
  def parse_from_json(json, credentials)
86
+ # The 'role' field is introduced after Conjur 4.9.0.0.
87
+ role = if ( role_json = json['role'] )
88
+ Role.new(Conjur::Authz::API.host, credentials)[Conjur::API.parse_role_id(role_json).join('/')]
89
+ else
90
+ nil
91
+ end
73
92
  member = Role.new(Conjur::Authz::API.host, credentials)[Conjur::API.parse_role_id(json['member']).join('/')]
74
93
  grantor = Role.new(Conjur::Authz::API.host, credentials)[Conjur::API.parse_role_id(json['grantor']).join('/')]
75
- RoleGrant.new(member, grantor, json['admin_option'])
94
+ RoleGrant.new(role, member, grantor, json['admin_option'])
76
95
  end
77
96
  end
78
97
  end
@@ -1,6 +1,9 @@
1
1
  require 'spec_helper'
2
+ require 'helpers/request_helpers'
2
3
 
3
4
  describe Conjur::API, api: :dummy do
5
+ include RequestHelpers
6
+
4
7
  describe '#create_resource' do
5
8
  it "passes to resource#create" do
6
9
  allow(api).to receive(:resource).with(:id).and_return(resource = double)
@@ -31,6 +34,12 @@ describe Conjur::API, api: :dummy do
31
34
  { 'id' => id }
32
35
  end
33
36
  }
37
+ it "counts resources" do
38
+ expect(Conjur::Resource).to receive(:all)
39
+ .with(host: authz_host, account: account, credentials: api.credentials, count: true).and_return(100)
40
+
41
+ expect(api.resources(count: true)).to eq(100)
42
+ end
34
43
  it "lists all resources" do
35
44
  expect(Conjur::Resource).to receive(:all)
36
45
  .with(host: authz_host, account: account, credentials: api.credentials).and_return(resources)
@@ -1,6 +1,9 @@
1
1
  require 'spec_helper'
2
+ require 'helpers/request_helpers'
2
3
 
3
4
  describe Conjur::Resource, api: :dummy, logging: :temp do
5
+ include RequestHelpers
6
+
4
7
  let(:account) { "the-account" }
5
8
  let(:uuid) { "ddd1f59a-494d-48fb-b045-0374c4a6eef9" }
6
9
 
@@ -59,6 +62,26 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
59
62
 
60
63
  expect(subject.permitted_roles("nuke")).to eq(['foo', 'bar'])
61
64
  end
65
+
66
+ it 'supports counting' do
67
+ expect_request(
68
+ method: :get,
69
+ url: "http://authz.example.com/some-account/roles/allowed_to/nuke/the-kind/resource-id?count=true",
70
+ headers: {}
71
+ ).and_return({count: 12}.to_json)
72
+
73
+ expect(subject.permitted_roles("nuke", count: true)).to eq(12)
74
+ end
75
+
76
+ it 'supports filtering' do
77
+ expect_request(
78
+ method: :get,
79
+ url: "http://authz.example.com/some-account/roles/allowed_to/nuke/the-kind/resource-id?search=hamsters",
80
+ headers: {}
81
+ ).and_return '["foo", "bar"]'
82
+
83
+ expect(subject.permitted_roles("nuke", search: 'hamsters')).to eq(['foo', 'bar'])
84
+ end
62
85
  end
63
86
 
64
87
  describe '#give_to' do
@@ -155,7 +178,7 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
155
178
  it "calls /account/resources" do
156
179
  expect_request(
157
180
  method: :get,
158
- url: "http://authz.example.com/the-account/resources",
181
+ url: "http://authz.example.com/the-account/resources/",
159
182
  headers: {}
160
183
  ).and_return '["foo", "bar"]'
161
184
 
@@ -165,7 +188,7 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
165
188
  it "can filter by owner" do
166
189
  expect_request(
167
190
  method: :get,
168
- url: "http://authz.example.com/the-account/resources/chunky?owner=alice",
191
+ url: "http://authz.example.com/the-account/resources/chunky/?owner=alice",
169
192
  headers: {}
170
193
  ).and_return '["foo", "bar"]'
171
194
 
@@ -176,7 +199,7 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
176
199
  it "can filter by kind" do
177
200
  expect_request(
178
201
  method: :get,
179
- url: "http://authz.example.com/the-account/resources/chunky",
202
+ url: "http://authz.example.com/the-account/resources/chunky/",
180
203
  headers: {}
181
204
  ).and_return '["foo", "bar"]'
182
205
 
@@ -184,11 +207,21 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
184
207
  .to eql(%w(foo bar))
185
208
  end
186
209
 
210
+ it "can count" do
211
+ expect_request(
212
+ method: :get,
213
+ url: "http://authz.example.com/the-account/resources/?count=true",
214
+ headers: {}
215
+ ).and_return({count: 12}.to_json)
216
+
217
+ expect(Conjur::Resource.all host: authz_host, account: account, count: true).to eq(12)
218
+ end
219
+
187
220
  it "passes search, limit, and offset params" do
188
221
  expect_request(
189
222
  method: :get,
190
223
  # Note that to_query sorts the keys
191
- url: "http://authz.example.com/the-account/resources?limit=5&offset=6&search=something",
224
+ url: "http://authz.example.com/the-account/resources/?limit=5&offset=6&search=something",
192
225
  headers: {}
193
226
  ).and_return '["foo", "bar"]'
194
227
  expect(Conjur::Resource.all(host: authz_host, account: account, search: 'something', limit:5, offset:6)).to eq(%w(foo bar))
@@ -197,7 +230,7 @@ describe Conjur::Resource, api: :dummy, logging: :temp do
197
230
  it "uses the given authz url" do
198
231
  expect_request(
199
232
  method: :get,
200
- url: "http://otherhost.example.com/the-account/resources",
233
+ url: "http://otherhost.example.com/the-account/resources/",
201
234
  headers: {}
202
235
  ).and_return '["foo", "bar"]'
203
236
 
@@ -2,9 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Conjur::RoleGrant, api: :dummy do
4
4
  describe '::parse_from_json' do
5
- it "creates member and grantor roles" do
6
- rg = Conjur::RoleGrant::parse_from_json({member: 'acc:k:r', grantor: 'acc:k:g', admin_option: true}.stringify_keys, {})
7
- expect(rg.member.url).to eq("#{authz_host}/acc/roles/k/r")
5
+ it "creates role, member and grantor roles" do
6
+ rg = Conjur::RoleGrant::parse_from_json({role: 'acc:k:r', member: 'acc:k:m', grantor: 'acc:k:g', admin_option: true}.stringify_keys, {})
7
+ expect(rg.role.url).to eq("#{authz_host}/acc/roles/k/r")
8
+ expect(rg.member.url).to eq("#{authz_host}/acc/roles/k/m")
8
9
  expect(rg.grantor.url).to eq("#{authz_host}/acc/roles/k/g")
9
10
  expect(rg.admin_option).to eq(true)
10
11
  end
@@ -93,7 +93,7 @@ describe Conjur::Role, api: :dummy do
93
93
  roles = ['foo:k:bar', 'baz:k:xyzzy']
94
94
  expect_request(
95
95
  method: :get,
96
- url: role.url + "/?all",
96
+ url: role.url + "/?all=true",
97
97
  headers: {}
98
98
  ).and_return roles.to_json
99
99
  all = role.all
@@ -102,6 +102,26 @@ describe Conjur::Role, api: :dummy do
102
102
  expect(all[1].account).to eq('baz')
103
103
  expect(all[1].id).to eq('xyzzy')
104
104
  end
105
+
106
+ it "handles 'count' parameter" do
107
+ expect_request(
108
+ method: :get,
109
+ url: role.url + "/?all=true&count=true",
110
+ headers: {}
111
+ ).and_return({count: 12}.to_json)
112
+ expect(role.all(count: true)).to eq(12)
113
+ end
114
+
115
+ describe "direct memberships" do
116
+ it 'routes to ?memberships' do
117
+ expect_request(
118
+ method: :get,
119
+ url: role.url + "/?memberships=true",
120
+ headers: {}
121
+ ).and_return("[]")
122
+ role.all(recursive: false)
123
+ end
124
+ end
105
125
 
106
126
  describe "filter param" do
107
127
  it "applies #cast to the filter" do
@@ -115,7 +135,7 @@ describe Conjur::Role, api: :dummy do
115
135
  it "calls ?all&#{query_string}" do
116
136
  expect_request(
117
137
  method: :get,
118
- url: role.url + "/?all&#{query_string}",
138
+ url: role.url + "/?all=true&#{query_string}",
119
139
  headers:{}
120
140
  ).and_return([].to_json)
121
141
  role.all filter: filter
@@ -186,11 +206,20 @@ describe Conjur::Role, api: :dummy do
186
206
  end
187
207
 
188
208
  describe '#members' do
209
+ it "can count the grants" do
210
+ expect_request(
211
+ method: :get,
212
+ url: role.url + "/?count=true&members=true"
213
+ ).and_return({count: 12}.to_json)
214
+
215
+ expect(subject.members(count: true)).to eq(12)
216
+ end
217
+
189
218
  it "gets ?members and turns each into RoleGrant" do
190
219
  grants = %w(foo bar)
191
220
  expect_request(
192
221
  method: :get,
193
- url: role.url + "/?members"
222
+ url: role.url + "/?members=true"
194
223
  ).and_return grants.to_json
195
224
  grants.each do |g|
196
225
  expect(Conjur::RoleGrant).to receive(:parse_from_json).with(g, anything).and_return g
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.29.2
4
+ version: 4.30.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: 2017-02-22 00:00:00.000000000 Z
12
+ date: 2017-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -402,6 +402,7 @@ files:
402
402
  - lib/conjur/log_source.rb
403
403
  - lib/conjur/path_based.rb
404
404
  - lib/conjur/pubkeys-api.rb
405
+ - lib/conjur/query_string.rb
405
406
  - lib/conjur/resource.rb
406
407
  - lib/conjur/role.rb
407
408
  - lib/conjur/role_grant.rb