conjur-api 4.13.0 → 4.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/configuration.rb
CHANGED
@@ -21,8 +21,42 @@
|
|
21
21
|
module Conjur
|
22
22
|
|
23
23
|
class << self
|
24
|
-
#
|
25
|
-
|
24
|
+
# Saves the current thread local {Conjur::Configuration},
|
25
|
+
# sets the thread local {Conjur::Configuration} to `config`, yields to the block, and ensures that
|
26
|
+
# the original thread local configuration is restored.
|
27
|
+
#
|
28
|
+
# Because Conjur configuration is accessed from the 'global' {Conjur.configuration} method by all Conjur
|
29
|
+
# API methods, this method provides the ability to set a thread local value for use within the current,
|
30
|
+
# or within a block in a single threaded application.
|
31
|
+
#
|
32
|
+
# Note that the {Conjur.configuration=} method sets the *global* {Conjur::Configuration}, not the thread-local
|
33
|
+
# value.
|
34
|
+
#
|
35
|
+
# @example Override Configuration in a Thread
|
36
|
+
# # in this rather contrived example, we'll override the {Conjur::Configuration#appliance_url} parameter
|
37
|
+
# # used by calls within a thread.
|
38
|
+
#
|
39
|
+
# # Set up the configuration in the main thread
|
40
|
+
# Conjur.configure do |c|
|
41
|
+
# # ...
|
42
|
+
# c.appliance_url = 'https://conjur.main-url.com/api'
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # Start a new thread that will perform requests to another server. In practice, you might
|
46
|
+
# # have a web server that uses a Conjur endpoint specified by a request header.
|
47
|
+
# Thread.new do
|
48
|
+
# Conjur.with_configuration Conjur.config.clone(appliance_url: 'https://conjur.local-url.com/api') do
|
49
|
+
# sleep 2
|
50
|
+
# puts "Thread local url is #{Conjur.config.appliance_url}"
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
# puts "Global url is #{Conjur.config.appliance_url}"
|
54
|
+
# # Outputs:
|
55
|
+
# Global url is https://conjur.main-url.com/api
|
56
|
+
# Thread local url is https://conjur.local-url.com/api
|
57
|
+
#
|
58
|
+
# @return [void]
|
59
|
+
def with_configuration(config)
|
26
60
|
oldvalue = Thread.current[:conjur_configuration]
|
27
61
|
Thread.current[:conjur_configuration] = config
|
28
62
|
yield
|
@@ -31,26 +65,123 @@ module Conjur
|
|
31
65
|
end
|
32
66
|
|
33
67
|
# Gets the current thread-local or global configuration.
|
68
|
+
#
|
69
|
+
# The thread-local Conjur configuration can only be set using the {Conjur.with_configuration}
|
70
|
+
# method. This method will try to return that value first, then the global configuration as
|
71
|
+
# set with {Conjur.configuration=} (which is lazily initialized if not set).
|
72
|
+
#
|
73
|
+
# @return [Conjur::Configuration] the thread-local or global Conjur configuration.
|
34
74
|
def configuration
|
35
75
|
Thread.current[:conjur_configuration] || (@config ||= Configuration.new)
|
36
76
|
end
|
37
77
|
|
38
78
|
# Sets the global configuration.
|
79
|
+
#
|
80
|
+
# This method *has no effect* on the thread local configuration. Use {Conjur.with_configuration} instead if
|
81
|
+
# that's what you want.
|
82
|
+
#
|
83
|
+
# @param [Conjur::Configuration] config the new configuration
|
84
|
+
# @return [Conjur::Configuration] the new value of the configuration
|
39
85
|
def configuration=(config)
|
40
86
|
@config = config
|
41
87
|
end
|
88
|
+
|
89
|
+
alias config configuration
|
90
|
+
alias config= configuration=
|
91
|
+
|
92
|
+
# Configure Conjur with a block.
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# Conjur.configure do |c|
|
96
|
+
# c.account = 'some-account'
|
97
|
+
# c.appliance_url = 'https://conjur.companyname.com/api'
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# @yieldparam [Conjur::Configuration] c the configuration instance to modify.
|
101
|
+
def configure
|
102
|
+
yield configuration
|
103
|
+
end
|
42
104
|
end
|
43
|
-
|
105
|
+
|
106
|
+
# Stores a configuration for the Conjur API client. This class provides *global* and *thread local* storage
|
107
|
+
# for common options used by the Conjur API. Most importantly, it specifies the
|
108
|
+
#
|
109
|
+
# * REST endpoints, derived from the {Conjur::Configuration#appliance_url} and {Conjur::Configuration#account} options
|
110
|
+
# * The certificate used for secure connections to the Conjur appliance ({Conjur::Configuration#cert_file})
|
111
|
+
#
|
112
|
+
# ### Environment Variables
|
113
|
+
#
|
114
|
+
# Option values used by Conjur can be given by environment variables, using a standard naming scheme. Specifically,
|
115
|
+
# an environment variable named `CONJUR_ACCOUNT` will be used to provide a default value for the {Conjur::Configuration#account}
|
116
|
+
# option.
|
117
|
+
#
|
118
|
+
#
|
119
|
+
# ### Required Options
|
120
|
+
#
|
121
|
+
# The {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url} are always required. Except in
|
122
|
+
# special cases, the {Conjur::Configuration#cert_file} is also required, but you may omit it if your Conjur root
|
123
|
+
# certificate is in the OpenSSl default certificate store.
|
124
|
+
#
|
125
|
+
# ### Thread Local Configuration
|
126
|
+
#
|
127
|
+
# While using a globally available configuration is convenient for most applications, sometimes you will need to
|
128
|
+
# use different configurations in different threads. This is supported by returning a thread local version from {Conjur.configuration}
|
129
|
+
# if one has been set by {Conjur.with_configuration}.
|
130
|
+
#
|
131
|
+
# @see Conjur.configuration
|
132
|
+
# @see Conjur.configure
|
133
|
+
# @see Conjur.with_configuration
|
134
|
+
#
|
135
|
+
# @example Basic Configuration
|
136
|
+
# Conjur.configure do |c|
|
137
|
+
# c.account = 'the-account'
|
138
|
+
# c.cert_file = find_conjur_cert_file
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# @example Setting the appliance_url from an environment variable
|
142
|
+
# ENV['CONJUR_APPLIANCE_URL'] = 'https://some-host.com/api'
|
143
|
+
# Conjur::Configuration.new.appliance_url # => 'https://some-host.com/api'
|
144
|
+
#
|
145
|
+
# @example Using thread local configuration in a web application request handler
|
146
|
+
# # Assume that we're in a request handler thread in a multithreaded web server.
|
147
|
+
#
|
148
|
+
# requested_appliance_url = request.header 'X-Conjur-Appliance-Url'
|
149
|
+
#
|
150
|
+
# with_configuration Conjur.config.clone(appliance_url: requested_appliance_url) do
|
151
|
+
# # `api` is an instance attribute. Note that we can use an api that was created
|
152
|
+
# # before we modified the thread local configuration.
|
153
|
+
#
|
154
|
+
#
|
155
|
+
# # 404 if the user doesn't exist
|
156
|
+
#
|
157
|
+
# user = api.user request.header('X-Conjur-Login')
|
158
|
+
# raise HttpError, 404, "User #{user.login} does not exist" unless user.exists?
|
159
|
+
# # ... finish the request
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
#
|
44
163
|
class Configuration
|
45
|
-
#
|
164
|
+
# @api private
|
46
165
|
attr_reader :explicit
|
47
|
-
|
48
|
-
#
|
166
|
+
|
167
|
+
# @api private
|
49
168
|
attr_reader :supplied
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
169
|
+
|
170
|
+
|
171
|
+
# Create a new {Conjur::Configuration}, setting initial values from
|
172
|
+
# `options`.
|
173
|
+
#
|
174
|
+
# @note `options` must use symbols for keys.
|
175
|
+
#
|
176
|
+
# @example
|
177
|
+
# Conjur.config = Conjur::Configuration.new account: 'companyname'
|
178
|
+
# Conjur.config.account # => 'companyname'
|
179
|
+
#
|
180
|
+
# @param [Hash] options hash of options to set on the new instance.
|
181
|
+
#
|
182
|
+
def initialize options = {}
|
183
|
+
@explicit = options.dup
|
184
|
+
@supplied = options.dup
|
54
185
|
end
|
55
186
|
|
56
187
|
class << self
|
@@ -112,44 +243,129 @@ module Conjur
|
|
112
243
|
end
|
113
244
|
end
|
114
245
|
|
115
|
-
#
|
116
|
-
|
246
|
+
# Return a copy of this {Conjur::Configuration} instance, optionally
|
247
|
+
# updating the copy with options from the `override_options` hash.
|
248
|
+
#
|
249
|
+
# @example
|
250
|
+
# original = Conjur.configuration
|
251
|
+
# original.account # => 'conjur'
|
252
|
+
# copy = original.clone account: 'some-other-account'
|
253
|
+
# copy.account # => 'some-other-account'
|
254
|
+
# original.account # => 'conjur'
|
255
|
+
#
|
256
|
+
# @param [Hash] override_options options to set on the new instance
|
257
|
+
# @return [Conjur::Configuration] a copy of this configuration
|
258
|
+
def clone override_options = {}
|
117
259
|
self.class.new self.explicit.dup.merge(override_options)
|
118
260
|
end
|
119
261
|
|
262
|
+
# Manually set an option. Note that setting an option not present in
|
263
|
+
# {Conjur::Configuration.accepted_options} is a no op.
|
264
|
+
# @api private
|
265
|
+
# @param [Symbol, String] key the name of the option to set
|
266
|
+
# @param [Object] value the option value.
|
120
267
|
def set(key, value)
|
121
268
|
if self.class.accepted_options.include?(key.to_sym)
|
122
269
|
explicit[key.to_sym] = value
|
123
270
|
supplied[key.to_sym] = value
|
124
271
|
end
|
125
272
|
end
|
126
|
-
|
273
|
+
|
274
|
+
# @!attribute authn_url
|
275
|
+
# The url for the {http://developer.conjur.net/reference/services/authentication Conjur authentication service}.
|
276
|
+
#
|
277
|
+
# @note You should not generally set this value. Instead, Conjur will derive it from the
|
278
|
+
# {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url}
|
279
|
+
# properties.
|
280
|
+
#
|
281
|
+
# @return [String] the authentication service url
|
127
282
|
add_option :authn_url do
|
128
283
|
account_service_url 'authn', 0
|
129
284
|
end
|
130
|
-
|
285
|
+
|
286
|
+
# @!attribute authz_url
|
287
|
+
# The url for the {http://developer.conjur.net/reference/services/authorization Conjur authorization service}.
|
288
|
+
#
|
289
|
+
# @note You should not generally set this value. Instead, Conjur will derive it from the
|
290
|
+
# {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url}
|
291
|
+
# properties.
|
292
|
+
#
|
293
|
+
# @return [String] the authorization service url
|
131
294
|
add_option :authz_url do
|
132
295
|
global_service_url 'authz', 100
|
133
296
|
end
|
134
297
|
|
298
|
+
# @!attribute core_url
|
299
|
+
# The url for the {http://developer.conjur.net/reference/services/directory Conjur core/directory service}.
|
300
|
+
#
|
301
|
+
# @note You should not generally set this value. Instead, Conjur will derive it from the
|
302
|
+
# {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url}
|
303
|
+
# properties.
|
304
|
+
#
|
305
|
+
# @return [String] the core/directory service url
|
135
306
|
add_option :core_url do
|
136
307
|
default_service_url 'core', 200
|
137
|
-
end
|
138
|
-
|
308
|
+
end
|
309
|
+
|
310
|
+
# @!attribute audit_url
|
311
|
+
# The url for the {http://developer.conjur.net/reference/services/audit Conjur audit service}.
|
312
|
+
#
|
313
|
+
# @note You should not generally set this value. Instead, Conjur will derive it from the
|
314
|
+
# {Conjur::Configuration#account} and {Conjur::Configuration#appliance_url}
|
315
|
+
# properties.
|
316
|
+
#
|
317
|
+
# @return [String] the audit service url
|
139
318
|
add_option :audit_url do
|
140
319
|
global_service_url 'audit', 300
|
141
320
|
end
|
142
|
-
|
321
|
+
|
322
|
+
# @!attribute appliance_url
|
323
|
+
# The url for your Conjur appliance.
|
324
|
+
#
|
325
|
+
# If your appliance's hostname is `'conjur.companyname.com'`, then your `appliance_url` will
|
326
|
+
# be `'https://conjur.companyname.com/api'`.
|
327
|
+
#
|
328
|
+
# @note If you are using an appliance (if you're not sure, you probably are), this option is *required*.
|
329
|
+
#
|
330
|
+
# @return [String] the appliance URL
|
143
331
|
add_option :appliance_url
|
144
|
-
|
332
|
+
|
333
|
+
# NOTE DO NOT DOCUMENT THIS AS AN ATTRIBUTE, IT IS PRIVATE AND YARD DOESN'T SUPPORT @api private ON ATTRIBUTES.
|
334
|
+
#
|
335
|
+
# The port used to derive ports for conjur services running locally. You will only use this if you are
|
336
|
+
# running the Conjur services locally, in which case you are probably a Conjur developer, and should ask
|
337
|
+
# someone in chat ;-)
|
338
|
+
#
|
145
339
|
add_option :service_base_port, default: 5000
|
146
340
|
|
341
|
+
# @!attribute account
|
342
|
+
# The organizational account used by Conjur.
|
343
|
+
#
|
344
|
+
# On Conjur appliances, this option will be set once when the appliance is first configured. You can get the
|
345
|
+
# value for the acccount option from your conjur administrator, or if you have installed
|
346
|
+
# the {http://developer.conjur.net/client_setup/cli.html Conjur command line tools} by running
|
347
|
+
# {http://developer.conjur.net/reference/services/authentication/whoami.html conjur authn whoami},
|
348
|
+
# or examining your {http://developer.conjur.net/client_setup/cli.html#Configure .conjurrc file}.
|
349
|
+
#
|
350
|
+
# @note this option is **required**, and attempting to make any api calls prior to setting it (either
|
351
|
+
# explicitly or with the `"CONJUR_ACCOUNT"` environment variable) will raise an exception.
|
352
|
+
#
|
353
|
+
# @return [String]
|
147
354
|
add_option :account, required: true
|
148
|
-
|
355
|
+
|
356
|
+
|
357
|
+
# @!attribute env
|
358
|
+
#
|
359
|
+
# The type of environment your program is running in (e.g., `development`, `production`, `test`).
|
360
|
+
#
|
361
|
+
# @deprecated
|
362
|
+
#
|
363
|
+
# @return [String] the environment name
|
149
364
|
add_option :env do
|
150
365
|
ENV['CONJUR_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "production"
|
151
366
|
end
|
152
|
-
|
367
|
+
|
368
|
+
# DEPRECATED SaaS option, do not doc comment!
|
153
369
|
add_option :stack do
|
154
370
|
case env
|
155
371
|
when "production"
|
@@ -159,6 +375,17 @@ module Conjur
|
|
159
375
|
end
|
160
376
|
end
|
161
377
|
|
378
|
+
# @!attribute cert_file
|
379
|
+
#
|
380
|
+
# Path to the certificate file to use when making secure connections to your Conjur appliance.
|
381
|
+
#
|
382
|
+
# This should be the path to the root Conjur SSL certificate in PEM format. You will normally get the
|
383
|
+
# certificate file using the {http://developer.conjur.net/reference/tools/utilities/init.html conjur init} command.
|
384
|
+
# This option is not required if the certificate or its root is in the OpenSSL default cert store.
|
385
|
+
# If your program throws an error indicating that SSL verification has failed, you probably need
|
386
|
+
# to set or fix this option.
|
387
|
+
#
|
388
|
+
# @return [String, nil] path to the certificate file, or nil if you aren't using one.
|
162
389
|
add_option :cert_file
|
163
390
|
|
164
391
|
private
|
data/lib/conjur/exists.rb
CHANGED
@@ -19,7 +19,33 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
# Provides an `exists?` method for things that may or may not exist.
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# Most conjur assets returned by `api.asset_name` methods (e.g., {Conjur::API#group}, {Conjur::API#user})
|
26
|
+
# may or may not exist. The {Conjur::Exists#exists?} method lets you determine whether or not such assets
|
27
|
+
# do in fact exist.
|
22
28
|
module Exists
|
29
|
+
|
30
|
+
# Check whether this asset exists by performing a HEAD request to its URL.
|
31
|
+
#
|
32
|
+
# This method will return false if the asset doesn't exist.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# does_not_exist = api.user 'does-not-exist' # This returns without error.
|
36
|
+
#
|
37
|
+
# # this is wrong!
|
38
|
+
# owner = does_not_exist.ownerid # raises RestClient::ResourceNotFound
|
39
|
+
#
|
40
|
+
# # this is right!
|
41
|
+
# owner = if does_not_exist.exists?
|
42
|
+
# does_not_exist.ownerid
|
43
|
+
# else
|
44
|
+
# nil # or some sensible default
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @param [Hash] options included for compatibility: **don't use this argument**!
|
48
|
+
# @return [Boolean] does it exist?
|
23
49
|
def exists?(options = {})
|
24
50
|
begin
|
25
51
|
self.head(options)
|
data/lib/conjur/group.rb
CHANGED
@@ -18,21 +18,67 @@
|
|
18
18
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
19
19
|
#
|
20
20
|
module Conjur
|
21
|
+
|
22
|
+
# A Conjur {http://developer.conjur.net/reference/services/directory/group Group} represents a collection of
|
23
|
+
# Conjur {http://developer.conjur.net/reference/services/directory/user Users}.
|
24
|
+
# This class represents Conjur group assets and operations on them.
|
25
|
+
#
|
26
|
+
# You should not create instances of this class directly. Instead, you can get them from
|
27
|
+
# API methods like {Conjur::API#group} and {Conjur::API#groups}.
|
28
|
+
#
|
21
29
|
class Group < RestClient::Resource
|
22
30
|
include ActsAsAsset
|
23
31
|
include ActsAsRole
|
24
|
-
|
32
|
+
|
33
|
+
# Add a user to the group or change whether an existing member can manage other members.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# # create an empty group
|
37
|
+
# group = api.create_group 'hoommans'
|
38
|
+
# # put a user in the group, with the ability to manage members
|
39
|
+
# group.add_member 'conjur:user:bob', admin_option: True
|
40
|
+
# # Hmm, bob is getting a little suspicious, better lower his privileges.
|
41
|
+
# group.add_member 'conjur:user:bob', admin_option: False
|
42
|
+
#
|
43
|
+
# # Notice that this method is idempotent:
|
44
|
+
# group.add_member 'alice'
|
45
|
+
# group.add_member 'alice' # Does nothing, alice is already a member
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# @param [String, Conjur::User, Conjur::Role] member the member to add. If a String is given, it must
|
49
|
+
# be a *fully qualified* Conjur id.
|
25
50
|
# @param [Hash] options
|
26
|
-
#
|
51
|
+
# @option options [Boolean] :admin_option (False) determines whether the member is able to manage members
|
52
|
+
# of this group.
|
53
|
+
# @return [void]
|
27
54
|
def add_member(member, options = {})
|
28
55
|
role.grant_to member, options
|
29
56
|
end
|
30
|
-
|
57
|
+
|
58
|
+
# Remove a member from this group.
|
59
|
+
#
|
60
|
+
# ### Notes
|
61
|
+
# * Unlike {#add_member}, this method is *not* idempotent.
|
62
|
+
# This means that calling it twice with the same user will raise a `RestClient::ResourceNotFound`
|
63
|
+
# exception.
|
64
|
+
# * The member may be represented as a *qualified* conjur id or a {Conjur::User} instance. Although
|
65
|
+
# it will accept anything that responds to `#roleid`, the behavior when adding or removing a non-user
|
66
|
+
# role is **undefined**.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# group = api.group 'admins'
|
70
|
+
# group.add_member 'bob'
|
71
|
+
# group.remove_member 'bob' # OK, bob is a member
|
72
|
+
# group.remove_member 'bob' # raises RestClient::ResourceNotFound
|
73
|
+
#
|
74
|
+
# @param [String, Conjur::User,Conjur::Role] member
|
75
|
+
# @return [void]
|
76
|
+
# @raise [RestClient::ResourceNotFound] when you try to remove a user who is not a member of the group.
|
31
77
|
def remove_member(member)
|
32
78
|
role.revoke_from member
|
33
79
|
end
|
34
80
|
|
35
|
-
# Update group properties
|
81
|
+
# Update group properties. Currently the only supported property is `:gidnumber`.
|
36
82
|
#
|
37
83
|
# @param [Hash] props new property values
|
38
84
|
# @option props [Integer] :gidnumber new GID number
|
@@ -19,27 +19,87 @@
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
20
|
#
|
21
21
|
module Conjur
|
22
|
+
# Many Conjur assets have key-value attributes. Although these should generally be accessed via
|
23
|
+
# methods on specific asset classes (for example, {Conjur::Resource#owner}), the are available as
|
24
|
+
# a `Hash` on all types supporting attributes.
|
22
25
|
module HasAttributes
|
26
|
+
# Returns this objects {#attributes}. This is primarily to support
|
27
|
+
# simple JSON serialization of Conjur assets.
|
28
|
+
#
|
29
|
+
# @param options [Hash,nil] unused, kept for compatibility reasons
|
30
|
+
# @see #attributes
|
23
31
|
def to_json(options = {})
|
24
32
|
attributes
|
25
33
|
end
|
26
34
|
|
27
|
-
|
35
|
+
# @api private
|
36
|
+
# Set the attributes for this Resource.
|
37
|
+
# @param [Hash] attributes new attributes for the object.
|
38
|
+
# @return [Hash] the new attributes
|
39
|
+
def attributes=(attributes); @attributes = attributes; end
|
40
|
+
|
41
|
+
# Get the attributes for this asset.
|
42
|
+
#
|
43
|
+
# Although the `Hash` returned by this method is mutable, you should treat as immutable unless you know
|
44
|
+
# exactly what you're doing. Each asset's attributes are constrained by a server side schema, which means
|
45
|
+
# that you will get an error if you violate the schema. and then try to save the asset.
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# @note this method will use a cached copy of the objects attributes instead of fetching them
|
49
|
+
# with each call. To ensure that the attributes are fresh, you can use the {#refresh} method
|
50
|
+
#
|
51
|
+
# @return [Hash] the asset's attributes.
|
28
52
|
def attributes
|
29
53
|
return @attributes if @attributes
|
30
54
|
fetch
|
31
55
|
end
|
32
|
-
|
56
|
+
|
57
|
+
|
58
|
+
# Update this asset's attributes on the server.
|
59
|
+
#
|
60
|
+
#
|
61
|
+
# @note If the objects attributes haven't been fetched (for example, by calling {#attributes}),
|
62
|
+
# this method is a no-op.
|
63
|
+
#
|
64
|
+
# Although you can manipulate an assets attributes and then call {#save}, the attributes are constrained
|
65
|
+
# by a server side schema, and attempting to set an attribute that doesn't exist will result in
|
66
|
+
# a 422 Unprocessable Entity error.
|
67
|
+
#
|
68
|
+
# If you want to set arbitrary metadata on an asset, you might consider using the {Conjur::Resource#tags}
|
69
|
+
# method instead.
|
70
|
+
#
|
71
|
+
# @return [void]
|
33
72
|
def save
|
34
|
-
|
73
|
+
if @attributes
|
74
|
+
self.put(attributes.to_json)
|
75
|
+
end
|
35
76
|
end
|
36
77
|
|
37
|
-
# Reload
|
78
|
+
# Reload this asset's attributes. This method can be used to guarantee a current view of the entity in the case
|
38
79
|
# that it has been modified by an update method or by an external party.
|
80
|
+
#
|
81
|
+
# @note any changes to {#attributes} without a call to #save will be overwritten by this method.
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# res = api.resources.firs
|
85
|
+
# res.attributes # => { ... }
|
86
|
+
# res.attributes['hello'] = 'blah'
|
87
|
+
# res.refresh
|
88
|
+
# res.attributes['hello'] # => nil
|
89
|
+
#
|
90
|
+
#
|
91
|
+
# @return [Hash] the asset's attributes.
|
39
92
|
def refresh
|
40
93
|
fetch
|
41
94
|
end
|
42
95
|
|
96
|
+
# Call a block that will perform actions that might change the asset's attributes.
|
97
|
+
# No matter what happens in the block, this method ensures that the cached attributes
|
98
|
+
# will be invalidated.
|
99
|
+
#
|
100
|
+
# @note this is mainly used internally, but included in the public api for completeness.
|
101
|
+
#
|
102
|
+
# @return [void]
|
43
103
|
def invalidate(&block)
|
44
104
|
yield
|
45
105
|
ensure
|
@@ -47,7 +107,8 @@ module Conjur
|
|
47
107
|
end
|
48
108
|
|
49
109
|
protected
|
50
|
-
|
110
|
+
# @api private
|
111
|
+
# Fetch the attributes, overwriting any current ones.
|
51
112
|
def fetch
|
52
113
|
@attributes = JSON.parse(get.body)
|
53
114
|
end
|