conjur-api 4.13.0 → 4.14.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 +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +0 -1
- data/README.md +59 -18
- data/conjur-api.gemspec +2 -1
- data/lib/conjur/acts_as_asset.rb +6 -9
- data/lib/conjur/acts_as_role.rb +54 -7
- data/lib/conjur/annotations.rb +52 -14
- data/lib/conjur/api/audit.rb +54 -8
- data/lib/conjur/api/authn.rb +54 -9
- data/lib/conjur/api/groups.rb +67 -4
- data/lib/conjur/api/hosts.rb +62 -3
- data/lib/conjur/api/layers.rb +48 -3
- data/lib/conjur/api/pubkeys.rb +140 -9
- data/lib/conjur/api.rb +13 -1
- data/lib/conjur/base.rb +93 -6
- data/lib/conjur/configuration.rb +247 -20
- data/lib/conjur/exists.rb +26 -0
- data/lib/conjur/group.rb +50 -4
- data/lib/conjur/has_attributes.rb +66 -5
- data/lib/conjur/resource.rb +155 -13
- data/lib/conjur/role.rb +1 -1
- data/lib/conjur/standard_methods.rb +7 -2
- data/lib/conjur/variable.rb +168 -6
- data/lib/conjur-api/version.rb +1 -1
- data/lib/conjur-api.rb +2 -0
- data/spec/api/authn_spec.rb +6 -3
- data/spec/api/groups_spec.rb +1 -1
- data/spec/api/pubkeys_spec.rb +4 -4
- data/spec/api/roles_spec.rb +4 -2
- data/spec/api/users_spec.rb +2 -2
- data/spec/api/variables_spec.rb +1 -1
- data/spec/helpers/errors_matcher.rb +34 -0
- data/spec/helpers/request_helpers.rb +10 -0
- data/spec/lib/annotations_spec.rb +5 -2
- data/spec/lib/audit_spec.rb +5 -5
- data/spec/lib/group_spec.rb +1 -1
- data/spec/lib/resource_spec.rb +11 -11
- data/spec/lib/role_spec.rb +8 -7
- data/spec/lib/user_spec.rb +7 -3
- data/spec/ssl_spec.rb +85 -0
- data/spec/standard_methods_helper.rb +2 -0
- data/spec/variable_spec.rb +5 -3
- metadata +34 -7
- data/lib/conjur/patches/rest-client.rb +0 -32
data/lib/conjur/resource.rb
CHANGED
@@ -21,28 +21,62 @@
|
|
21
21
|
require 'conjur/annotations'
|
22
22
|
|
23
23
|
module Conjur
|
24
|
+
|
25
|
+
# A `Conjur::Resource` instance represents a Conjur
|
26
|
+
# {http://developer.conjur.net/reference/services/authorization/resource Resource}.
|
27
|
+
#
|
28
|
+
# You should not instantiate this class directly. Instead, you can get an instance from the
|
29
|
+
# {Conjur::API#resource} and {Conjur::API#resources} methods, or from the {ActsAsResource#resource} method
|
30
|
+
# present on objects representing Conjur assets that have associated resources.
|
31
|
+
#
|
24
32
|
class Resource < RestClient::Resource
|
25
33
|
include HasAttributes
|
26
34
|
include PathBased
|
27
35
|
include Exists
|
28
|
-
|
36
|
+
|
37
|
+
# The identifier part of the `resource_id` for this resource. The identifier
|
38
|
+
# is the resource id without the `account` and `kind` parts.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# resource = api.resource 'conjur:layer:pubkeys-1.0/public-keys'
|
42
|
+
# resource.identifier # => 'pubkeys-1.0/public-keys'
|
43
|
+
#
|
44
|
+
# @return [String] the identifier part of the id.
|
29
45
|
def identifier
|
30
46
|
match_path(3..-1)
|
31
47
|
end
|
32
|
-
|
48
|
+
|
49
|
+
# The full role id of the role that owns this resource.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# api.current_role # => 'conjur:user:jon'
|
53
|
+
# resource = api.create_resource 'conjur:example:resource-owner'
|
54
|
+
# resource.owner # => 'conjur:user:jon'
|
55
|
+
#
|
56
|
+
# @return [String] the full role id of this resource's owner.
|
33
57
|
def ownerid
|
34
58
|
attributes['owner']
|
35
59
|
end
|
36
60
|
|
37
61
|
alias owner ownerid
|
38
62
|
|
39
|
-
#
|
63
|
+
# Return the full id for this resource. The format is `account:kind:identifier`
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# resource = api.layer('pubkeys-1.0/public-keys').resource
|
67
|
+
# resource.account # => 'conjur'
|
68
|
+
# resource.kind # => 'layer'
|
69
|
+
# resource.identifier # => 'pubkeys-1.0/public-keys'
|
70
|
+
# resource.resourceid # => 'conjur:layer:pubkeys-1.0/public-keys'
|
71
|
+
# @return [String]
|
40
72
|
def resourceid
|
41
73
|
[account, kind, identifier].join ':'
|
42
74
|
end
|
43
75
|
|
44
76
|
alias :resource_id :resourceid
|
45
|
-
|
77
|
+
|
78
|
+
|
79
|
+
# @api private
|
46
80
|
def create(options = {})
|
47
81
|
log do |logger|
|
48
82
|
logger << "Creating resource #{resourceid}"
|
@@ -54,16 +88,42 @@ module Conjur
|
|
54
88
|
end
|
55
89
|
|
56
90
|
# Lists roles that have a specified permission on the resource.
|
91
|
+
#
|
92
|
+
# This will return only roles of which api.current_user is a member.
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# resource = api.resource 'conjur:variable:example'
|
96
|
+
# resource.permitted_roles 'execute' # => ['conjur:user:admin']
|
97
|
+
# resource.permit 'execute', api.user('jon')
|
98
|
+
# resource.permitted_roles 'execute' # => ['conjur:user:admin', 'conjur:user:jon']
|
99
|
+
#
|
100
|
+
# @param permission [String] the permission``
|
101
|
+
# @param options [Hash, nil] extra options to pass to RestClient::Resource#get
|
102
|
+
# @return [Array<String>] the ids of roles that have `permission` on this resource.
|
57
103
|
def permitted_roles(permission, options = {})
|
58
104
|
JSON.parse RestClient::Resource.new(Conjur::Authz::API.host, self.options)["#{account}/roles/allowed_to/#{permission}/#{path_escape kind}/#{path_escape identifier}"].get(options)
|
59
105
|
end
|
60
106
|
|
61
|
-
# Changes the owner of a resource
|
107
|
+
# Changes the owner of a resource. You must be the owner of the resource
|
108
|
+
# or a member of the owner role to do this.
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# resource.owner # => 'conjur:user:admin'
|
112
|
+
# resource.give_to 'conjur:user:jon'
|
113
|
+
# resource.owner # => 'conjur:user:jon'
|
114
|
+
#
|
115
|
+
# @param owner [String, #roleid] the new owner.
|
116
|
+
# @return [void]
|
62
117
|
def give_to(owner, options = {})
|
63
118
|
owner = cast(owner, :roleid)
|
64
|
-
|
119
|
+
invalidate do
|
120
|
+
self.put(options.merge(owner: owner))
|
121
|
+
end
|
122
|
+
|
123
|
+
nil
|
65
124
|
end
|
66
125
|
|
126
|
+
# @api private
|
67
127
|
def delete(options = {})
|
68
128
|
log do |logger|
|
69
129
|
logger << "Deleting resource #{resourceid}"
|
@@ -74,6 +134,32 @@ module Conjur
|
|
74
134
|
super options
|
75
135
|
end
|
76
136
|
|
137
|
+
# Grant `privilege` on this resource to `role`.
|
138
|
+
#
|
139
|
+
# This operation is idempotent, that is, nothing will happen if
|
140
|
+
# you attempt to grant a privilege that the role already has on
|
141
|
+
# this resource.
|
142
|
+
#
|
143
|
+
# @example
|
144
|
+
# user = api.user 'bob'
|
145
|
+
# resource = api.variable('example').resource
|
146
|
+
# resource.permitted_roles 'bake' # => ['conjur:user:admin']
|
147
|
+
# resource.permit 'fry', user
|
148
|
+
# resource.permitted_roles 'fry' # => ['conjur:user:admin', 'conjur:user:bob']
|
149
|
+
# resource.permit ['boil', 'bake'], bob
|
150
|
+
# resource.permitted_roles 'boil' # => ['conjur:user:admin', 'conjur:user:bob']
|
151
|
+
# resource.permitted_roles 'bake' # => ['conjur:user:admin', 'conjur:user:bob']
|
152
|
+
#
|
153
|
+
# @param privilege [String, #each] The privilege to grant, for example
|
154
|
+
# `'execute'`, `'read'`, or `'update'`. You may also pass an `Enumerable`
|
155
|
+
# object, in which case the Strings yielded by #each will all be granted
|
156
|
+
#
|
157
|
+
# @param role [String, #roleid] The role-ish object or full role id
|
158
|
+
# to which the permission is to be granted/
|
159
|
+
#
|
160
|
+
# @param options [Hash, nil] options to pass through to `RestClient::Resource#post`
|
161
|
+
#
|
162
|
+
# @return [void]
|
77
163
|
def permit(privilege, role, options = {})
|
78
164
|
role = cast(role, :roleid)
|
79
165
|
eachable(privilege).each do |p|
|
@@ -91,8 +177,24 @@ module Conjur
|
|
91
177
|
raise $! unless $!.http_body == "Privilege already granted."
|
92
178
|
end
|
93
179
|
end
|
180
|
+
nil
|
94
181
|
end
|
95
|
-
|
182
|
+
|
183
|
+
# The inverse operation of `#permit`. Deny permission `privilege` to `role`
|
184
|
+
# on this resource.
|
185
|
+
#
|
186
|
+
# @example
|
187
|
+
#
|
188
|
+
# resource.permitted_roles 'execute' # => ['conjur:user:admin', 'conjur:user:alice']
|
189
|
+
# resource.deny 'execute', 'conjur:user:alice'
|
190
|
+
# resource.permitted_roles 'execute' # => ['conjur:user:admin']
|
191
|
+
#
|
192
|
+
# @param privilege [String, #each] A permission name or an `Enumerable` of permissions to deny. In the
|
193
|
+
# later, all permissions will be denied.
|
194
|
+
#
|
195
|
+
# @param role [String, :roleid] A full role id or a role-ish object whose permissions we will deny.
|
196
|
+
#
|
197
|
+
# @return [void]
|
96
198
|
def deny(privilege, role, options = {})
|
97
199
|
role = cast(role, :roleid)
|
98
200
|
eachable(privilege).each do |p|
|
@@ -104,11 +206,27 @@ module Conjur
|
|
104
206
|
end
|
105
207
|
self["?deny&privilege=#{query_escape p}&role=#{query_escape role}"].post(options)
|
106
208
|
end
|
209
|
+
nil
|
107
210
|
end
|
108
211
|
|
109
|
-
# True if the logged-in role, or a role specified using the
|
212
|
+
# True if the logged-in role, or a role specified using the :acting_as option, has the
|
110
213
|
# specified +privilege+ on this resource.
|
214
|
+
#
|
215
|
+
# @example
|
216
|
+
# api.current_role # => 'conjur:cat:mouse'
|
217
|
+
# resource.permitted_roles 'execute' # => ['conjur:user:admin', 'conjur:cat:mouse']
|
218
|
+
# resource.permitted_roles 'update', # => ['conjur:user:admin', 'conjur:cat:gino']
|
219
|
+
#
|
220
|
+
# resource.permitted? 'update' # => false, `mouse` can't update this resource
|
221
|
+
# resource.permitted? 'execute' # => true, `mouse` can execute it.
|
222
|
+
# resource.permitted? 'update',acting_as: 'conjur:cat:gino' # => true, `gino` can update it.
|
223
|
+
# @param privilege [String] the privilege to check
|
224
|
+
# @param [Hash, nil] options for the request
|
225
|
+
# @option options [String,nil] :acting_as check whether the role given by this full role id is permitted
|
226
|
+
# instead of checking +api.current_role+.
|
227
|
+
# @return [Boolean]
|
111
228
|
def permitted?(privilege, options = {})
|
229
|
+
# TODO this method should accept an optional role rather than putting it in the options hash.
|
112
230
|
params = {
|
113
231
|
check: true,
|
114
232
|
privilege: query_escape(privilege)
|
@@ -121,15 +239,31 @@ module Conjur
|
|
121
239
|
rescue RestClient::ResourceNotFound
|
122
240
|
false
|
123
241
|
end
|
124
|
-
|
125
|
-
# Return
|
242
|
+
|
243
|
+
# Return an {Conjur::Annotations} object to manipulate and view annotations.
|
244
|
+
#
|
245
|
+
# @see Conjur::Annotations
|
246
|
+
# @example
|
247
|
+
# resource.annotations.count # => 0
|
248
|
+
# resource.annotations['foo'] = 'bar'
|
249
|
+
# resource.annotations.each do |k,v|
|
250
|
+
# puts "#{k}=#{v}"
|
251
|
+
# end
|
252
|
+
# # output is
|
253
|
+
# # foo=bar
|
254
|
+
#
|
255
|
+
#
|
256
|
+
# @return [Conjur::Annotations]
|
126
257
|
def annotations
|
127
258
|
@annotations ||= Conjur::Annotations.new(self)
|
128
259
|
end
|
129
260
|
alias tags annotations
|
130
261
|
|
131
|
-
#
|
132
|
-
#
|
262
|
+
# @api private
|
263
|
+
# This is documented by Conjur::API#resources.
|
264
|
+
# Returns all resources (optionally qualified by kind) visible to the user with given credentials.
|
265
|
+
#
|
266
|
+
#
|
133
267
|
# Options are:
|
134
268
|
# - host - authz url,
|
135
269
|
# - credentials,
|
@@ -154,7 +288,15 @@ module Conjur
|
|
154
288
|
end
|
155
289
|
|
156
290
|
protected
|
157
|
-
|
291
|
+
|
292
|
+
|
293
|
+
# Given an Object, return something that responds to each. In particular,
|
294
|
+
# if `item.respond_to? :each` is true, we return the item, otherwise we put it in
|
295
|
+
# an array.
|
296
|
+
#
|
297
|
+
# @param item [Object] the value we want to each over.
|
298
|
+
# @return [#each] `item` if item is already eachable, otherwise `[item]`.
|
299
|
+
# @api private
|
158
300
|
def eachable(item)
|
159
301
|
item.respond_to?(:each) ? item : [ item ]
|
160
302
|
end
|
data/lib/conjur/role.rb
CHANGED
@@ -23,6 +23,9 @@ require 'active_support/dependencies/autoload'
|
|
23
23
|
require 'active_support/core_ext'
|
24
24
|
|
25
25
|
module Conjur
|
26
|
+
# @api private
|
27
|
+
# This module provides a number of "standard" `REST` helpers,
|
28
|
+
# to wit, create, list and show.
|
26
29
|
module StandardMethods
|
27
30
|
|
28
31
|
protected
|
@@ -43,10 +46,12 @@ module Conjur
|
|
43
46
|
|
44
47
|
def standard_list(host, type, options)
|
45
48
|
JSON.parse(RestClient::Resource.new(host, credentials)[type.to_s.pluralize].get(options)).collect do |item|
|
49
|
+
# Note that we don't want to fully_escape the ids below -- methods like #layer, #host, etc don't expect
|
50
|
+
# ids to be escaped, and will escape them again!.
|
46
51
|
if item.is_a? String # lists w/o details are just list of ids
|
47
|
-
send(type,
|
52
|
+
send(type,item)
|
48
53
|
else # list w/ details consists of hashes
|
49
|
-
send(type,
|
54
|
+
send(type, item['id']).tap { |obj| obj.attributes=item }
|
50
55
|
end
|
51
56
|
end
|
52
57
|
end
|
data/lib/conjur/variable.rb
CHANGED
@@ -19,12 +19,137 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
|
23
|
+
# Secrets stored in Conjur are represented by {http://developer.conjur.net/reference/services/directory/variable Variables}.
|
24
|
+
# The code responsible for the actual encryption of variables is open source as part of the
|
25
|
+
# {https://github.com/conjurinc/slosilo Slosilo} library.
|
26
|
+
#
|
27
|
+
# You should not generally create instances of this class directly. Instead, you can get them from
|
28
|
+
# {Conjur::API} methods such as {Conjur::API#create_variable} and {Conjur::API#variable}.
|
29
|
+
#
|
30
|
+
# Conjur variables store metadata (mime-type and secret kind) with each secret.
|
31
|
+
#
|
32
|
+
# Variables are *versioned*. Storing secrets in multiple places is a bad security practice, but
|
33
|
+
# overwriting a secret accidentally can create a major problem for development and ops teams. Conjur
|
34
|
+
# discourages bad security practices while avoiding ops disasters by storing all previous versions of
|
35
|
+
# a secret.
|
36
|
+
#
|
37
|
+
# ### Important
|
38
|
+
# A common pitfall when trying to access older versions of a variable is to assume that `0` is the oldest
|
39
|
+
# version. In fact, `0` references the *latest* version, while *1* is the oldest.
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# ### Permissions
|
43
|
+
#
|
44
|
+
# * To *read* the value of a `variable`, you must have permission to `'execute'` the variable.
|
45
|
+
# * To *add* a value to a `variable`, you must have permission to `'update'` the variable.
|
46
|
+
# * To *show* metadata associated with a variable, but *not* the value of the secret, you must have `'read'`
|
47
|
+
# permission on the variable.
|
48
|
+
#
|
49
|
+
# When you create a secret, the creator role is granted all three of the above permissions.
|
50
|
+
#
|
51
|
+
# @example Get a variable and access its metadata and the latest value
|
52
|
+
# variable = api.variable 'example'
|
53
|
+
# puts variable.kind # "example-secret"
|
54
|
+
# puts variable.mime_type # "text/plain"
|
55
|
+
# puts variable.value # "supahsecret"
|
56
|
+
#
|
57
|
+
# @example Variable permissions
|
58
|
+
# # use our 'admin' api to create a variable 'permissions-example
|
59
|
+
# admin_var = admin_api.create_variable 'text/plain', 'example', 'permissions-example'
|
60
|
+
#
|
61
|
+
# # get a 'view' to it from user 'alice'
|
62
|
+
# alice_var = alice_api.variable admin_var.id
|
63
|
+
#
|
64
|
+
# # Initilally, all of the following raise a RestClient::Forbidden exception
|
65
|
+
# alice_var.attributes
|
66
|
+
# alice_var.value
|
67
|
+
# alice_var.add_value 'hi'
|
68
|
+
#
|
69
|
+
# # Allow alice to see the variables attributes
|
70
|
+
# admin_var.permit 'read', alice
|
71
|
+
# alice_var.attributes # OK
|
72
|
+
#
|
73
|
+
# # Allow alice to update the variable
|
74
|
+
# admin_var.permit 'update', alice
|
75
|
+
# alice_var.add_value 'hello'
|
76
|
+
#
|
77
|
+
# # Notice that alice still can't see the variable's value:
|
78
|
+
# alice_var.value # raises RestClient::Forbidden
|
79
|
+
#
|
80
|
+
# # Finally, we let alice execute the variable
|
81
|
+
# admin_var.permit 'execute', alice
|
82
|
+
# alice_var.value # 'hello'
|
83
|
+
#
|
84
|
+
# @example Variables are versioned
|
85
|
+
# var = api.variable 'version-example'
|
86
|
+
# # Unless you set a variables value when you create it, the variable starts out without a value and version_count
|
87
|
+
# # is 0.
|
88
|
+
# var.version_count # => 0
|
89
|
+
# var.value # raises RestClient::ResourceNotFound (404)
|
90
|
+
#
|
91
|
+
# # Add a value
|
92
|
+
# var.add_value 'value 1'
|
93
|
+
# var.version_count # => 1
|
94
|
+
# var.value # => 'value 1'
|
95
|
+
#
|
96
|
+
# # Add another value
|
97
|
+
# var.add_value 'value 2'
|
98
|
+
# var.version_count # => 2
|
99
|
+
#
|
100
|
+
# # 'value' with no argument returns the most recent value
|
101
|
+
# var.value # => 'value 2'
|
102
|
+
#
|
103
|
+
# # We can access older versions by their 1 based index:
|
104
|
+
# var.value 1 # => 'value 1'
|
105
|
+
# var.value 2 # => 'value 2'
|
106
|
+
# # Notice that version 0 of a variable is always the most recent:
|
107
|
+
# var.value 0 # => 'value 2'
|
108
|
+
#
|
22
109
|
class Variable < RestClient::Resource
|
23
110
|
include ActsAsAsset
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
111
|
+
|
112
|
+
# The kind of secret represented by this variable, for example, `'postgres-url'` or
|
113
|
+
# `'aws-secret-access-key'`.
|
114
|
+
#
|
115
|
+
# You must have the **`'read'`** permission on a variable to call this method.
|
116
|
+
#
|
117
|
+
# This attribute is only for human consumption, and does not take part in the Conjur permissions
|
118
|
+
# model.
|
119
|
+
#
|
120
|
+
# @note this is **not** the same as the `kind` part of a qualified Conjur id.
|
121
|
+
# @return [String] a string representing the kind of secret.
|
122
|
+
def kind
|
123
|
+
attributes['kind']
|
124
|
+
end
|
125
|
+
|
126
|
+
# The MIME Type of the variable's value.
|
127
|
+
#
|
128
|
+
# You must have the **`'read'`** permission on a variable to call this method.
|
129
|
+
#
|
130
|
+
# This attribute is used by the Conjur services to set a response `Content-Type` header when
|
131
|
+
# returning the value of a variable. Conjur applies the same MIME Type to all versions of a variable,
|
132
|
+
# so if you plan on accessing the variable in a way that depends on a correct `Content-Type` header
|
133
|
+
# you should make sure to store appropriate data for the mime type in all versions.
|
134
|
+
#
|
135
|
+
# @return [String] a MIME type, such as `'text/plain'` or `'application/octet-stream'`.
|
136
|
+
def mime_type
|
137
|
+
attributes['mime_type']
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add a new value to the variable.
|
141
|
+
#
|
142
|
+
# You must have the **`'update'`** permission on a variable to call this method.
|
143
|
+
#
|
144
|
+
# @example Add a value to a variable
|
145
|
+
# var = api.variable 'my-secret'
|
146
|
+
# puts var.version_count # 1
|
147
|
+
# puts var.value # 'supersecret'
|
148
|
+
# var.add_value "new_secret"
|
149
|
+
# puts var.version_count # 2
|
150
|
+
# puts var.value # 'new_secret'
|
151
|
+
# @param [String] value the new value to add
|
152
|
+
# @return [void]
|
28
153
|
def add_value value
|
29
154
|
log do |logger|
|
30
155
|
logger << "Adding a value to variable #{id}"
|
@@ -33,11 +158,48 @@ module Conjur
|
|
33
158
|
self['values'].post value: value
|
34
159
|
end
|
35
160
|
end
|
36
|
-
|
161
|
+
|
162
|
+
# Return the number of versions of the variable.
|
163
|
+
#
|
164
|
+
# You must have the **`'read'`** permission on a variable to call this method.
|
165
|
+
#
|
166
|
+
# @example
|
167
|
+
# var.version_count # => 4
|
168
|
+
# var.add_value "something new"
|
169
|
+
# var.version_count # => 5
|
170
|
+
#
|
171
|
+
# @return [Integer] the number of versions
|
37
172
|
def version_count
|
38
173
|
self.attributes['version_count']
|
39
174
|
end
|
40
|
-
|
175
|
+
|
176
|
+
# Return the version of a variable.
|
177
|
+
#
|
178
|
+
# You must have the **`'execute'`** permission on a variable to call this method.
|
179
|
+
#
|
180
|
+
# When no argument is given, the most recent version is returned.
|
181
|
+
#
|
182
|
+
# When a `version` argument is given, the method returns a version according to the following rules:
|
183
|
+
# * If `version` is 0, the *most recent* version is returned.
|
184
|
+
# * If `version` is less than 0 or greater than {#version_count}, a `RestClient::ResourceNotFound` exception
|
185
|
+
# will be raised.
|
186
|
+
# * If {#version_count} is 0, a `RestClient::ResourceNotFound` exception will be raised.
|
187
|
+
# * If `version` is >= 1 and `version` <= {#version_count}, the version at the **1 based** index given by `version`
|
188
|
+
# will be returned.
|
189
|
+
#
|
190
|
+
# @example Fetch all versions of a variable
|
191
|
+
# versions = (1..var.version_count).map do |version|
|
192
|
+
# var.value version
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# @example Get the current version of a variable
|
196
|
+
# # All of these return the same thing:
|
197
|
+
# var.value
|
198
|
+
# var.value 0
|
199
|
+
# var.value var.version_count
|
200
|
+
#
|
201
|
+
# @param [Integer] version the **1 based** version.
|
202
|
+
# @return [String] the value of the variable
|
41
203
|
def value(version = nil)
|
42
204
|
url = 'value'
|
43
205
|
url << "?version=#{version}" if version
|
data/lib/conjur-api/version.rb
CHANGED
data/lib/conjur-api.rb
ADDED
data/spec/api/authn_spec.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'cas_rest_client'
|
3
|
+
require 'helpers/request_helpers'
|
3
4
|
|
4
5
|
describe Conjur::API do
|
6
|
+
include RequestHelpers
|
7
|
+
|
5
8
|
let(:host) { 'http://authn.example.com' }
|
6
9
|
let(:user) { 'kmitnick' }
|
7
10
|
let(:password) { 'sikret' }
|
@@ -12,7 +15,7 @@ describe Conjur::API do
|
|
12
15
|
|
13
16
|
describe "::login" do
|
14
17
|
it "gets /users/login" do
|
15
|
-
|
18
|
+
expect_request(
|
16
19
|
method: :get, url: "http://authn.example.com/users/login",
|
17
20
|
user: user,
|
18
21
|
password: password,
|
@@ -41,7 +44,7 @@ describe Conjur::API do
|
|
41
44
|
|
42
45
|
describe "::authenticate" do
|
43
46
|
it "posts the password and dejsons the result" do
|
44
|
-
|
47
|
+
expect_request(
|
45
48
|
method: :post, url: "http://authn.example.com/users/#{user}/authenticate",
|
46
49
|
payload: password, headers: { content_type: 'text/plain' }
|
47
50
|
).and_return '{ "response": "foo"}'
|
@@ -51,7 +54,7 @@ describe Conjur::API do
|
|
51
54
|
|
52
55
|
describe "::update_password" do
|
53
56
|
it "logs in and puts the new password" do
|
54
|
-
|
57
|
+
expect_request(
|
55
58
|
method: :put,
|
56
59
|
url: "http://authn.example.com/users/password",
|
57
60
|
user: user,
|
data/spec/api/groups_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe Conjur::API, api: :dummy do
|
|
28
28
|
|
29
29
|
describe '#find_groups' do
|
30
30
|
it "searches the group by GID" do
|
31
|
-
|
31
|
+
expect_request(
|
32
32
|
method: :get,
|
33
33
|
url: "#{core_host}/groups/search?gidnumber=12345",
|
34
34
|
headers: credentials[:headers]
|
data/spec/api/pubkeys_spec.rb
CHANGED
@@ -32,7 +32,7 @@ describe Conjur::API, api: :dummy do
|
|
32
32
|
|
33
33
|
describe "#public_keys" do
|
34
34
|
it "GETs /:username" do
|
35
|
-
|
35
|
+
expect_request(
|
36
36
|
url: pubkeys_url_for("bob"),
|
37
37
|
method: :get,
|
38
38
|
headers: credentials[:headers],
|
@@ -43,7 +43,7 @@ describe Conjur::API, api: :dummy do
|
|
43
43
|
|
44
44
|
describe "#add_public_key" do
|
45
45
|
it "POSTs /:username with the data" do
|
46
|
-
|
46
|
+
expect_request(
|
47
47
|
url: pubkeys_url_for("bob"),
|
48
48
|
method: :post,
|
49
49
|
headers: credentials[:headers],
|
@@ -55,7 +55,7 @@ describe Conjur::API, api: :dummy do
|
|
55
55
|
|
56
56
|
describe "#delete_public_key" do
|
57
57
|
it "DELETEs /:username/:keyname" do
|
58
|
-
|
58
|
+
expect_request(
|
59
59
|
url: pubkeys_url_for("bob", "bob-key"),
|
60
60
|
method: :delete,
|
61
61
|
headers: credentials[:headers]
|
@@ -63,4 +63,4 @@ describe Conjur::API, api: :dummy do
|
|
63
63
|
api.delete_public_key("bob", "bob-key")
|
64
64
|
end
|
65
65
|
end
|
66
|
-
end
|
66
|
+
end
|
data/spec/api/roles_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'helpers/request_helpers'
|
2
3
|
|
3
4
|
describe Conjur::API, api: :dummy do
|
5
|
+
include RequestHelpers
|
4
6
|
subject { api }
|
5
7
|
|
6
8
|
describe 'role_graph' do
|
@@ -34,8 +36,8 @@ describe Conjur::API, api: :dummy do
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def expect_request_with_params params={}
|
37
|
-
|
38
|
-
|
39
|
+
expect_request(headers: credentials[:headers], method: :get,
|
40
|
+
url: role_graph_url_for(roles, options, current_role))
|
39
41
|
.and_return(response)
|
40
42
|
end
|
41
43
|
|
data/spec/api/users_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Conjur::API, api: :dummy do
|
|
11
11
|
describe 'user#update' do
|
12
12
|
let(:userid) { "alice@wonderland" }
|
13
13
|
it "PUTs to /users/:id?uidnumber=:uidnumber" do
|
14
|
-
|
14
|
+
expect_request(
|
15
15
|
method: :put,
|
16
16
|
url: "#{core_host}/users/#{api.fully_escape(userid)}",
|
17
17
|
headers: credentials[:headers],
|
@@ -28,7 +28,7 @@ describe Conjur::API, api: :dummy do
|
|
28
28
|
let(:search_result) { ["someuser"].to_json }
|
29
29
|
|
30
30
|
it "GETs /users/search with appropriate options, and returns parsed JSON response" do
|
31
|
-
|
31
|
+
expect_request(
|
32
32
|
method: :get,
|
33
33
|
url: "#{core_host}/users/search?uidnumber=12345",
|
34
34
|
headers: credentials[:headers]
|
data/spec/api/variables_spec.rb
CHANGED
@@ -25,7 +25,7 @@ describe Conjur::API, api: :dummy do
|
|
25
25
|
shared_context "Stubbed API" do
|
26
26
|
let (:expected_url) { "#{core_host}/variables/values?vars=#{varlist.map {|v| api.fully_escape(v) }.join(",")}" }
|
27
27
|
before {
|
28
|
-
|
28
|
+
expect_request(
|
29
29
|
method: :get,
|
30
30
|
url: expected_url,
|
31
31
|
headers: credentials[:headers]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rspec/expectations'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :raise_one_of do |*exn_classes|
|
4
|
+
supports_block_expectations
|
5
|
+
|
6
|
+
match do |block|
|
7
|
+
expect(&block).to raise_error do |error|
|
8
|
+
@actual_error = error
|
9
|
+
expect(exn_classes).to include error.class
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
failure_message do
|
14
|
+
"expected #{expected_error}#{given_error}"
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method :expected_error do
|
18
|
+
"one of " + exn_classes.join(', ')
|
19
|
+
end
|
20
|
+
|
21
|
+
def given_error
|
22
|
+
return " but nothing was raised" unless @actual_error
|
23
|
+
backtrace = format_backtrace(@actual_error.backtrace)
|
24
|
+
[
|
25
|
+
", got #{@actual_error.inspect} with backtrace:",
|
26
|
+
*backtrace
|
27
|
+
].join("\n # ")
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_backtrace backtrace
|
31
|
+
formatter = RSpec::Matchers.configuration.backtrace_formatter
|
32
|
+
formatter.format_backtrace(backtrace)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Helpers for REST client tests
|
2
|
+
module RequestHelpers
|
3
|
+
def expect_request details, &block
|
4
|
+
expect(RestClient::Request).to receive(:execute).with(hash_including(details), &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def allow_request details, &block
|
8
|
+
allow(RestClient::Request).to receive(:execute).with(hash_including(details), &block)
|
9
|
+
end
|
10
|
+
end
|