conjur-api 4.29.2 → 4.30.0

Sign up to get free protection for your applications and to get access to all the features.
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