conjur-api 5.3.0 → 5.3.5

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +10 -0
  3. data/.github/CODEOWNERS +10 -0
  4. data/.github/ISSUE_TEMPLATE/bug.md +42 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  6. data/.github/PULL_REQUEST_TEMPLATE.md +21 -0
  7. data/.gitignore +1 -0
  8. data/.gitleaks.toml +219 -0
  9. data/.overcommit.yml +16 -0
  10. data/.rubocop.yml +3 -0
  11. data/.rubocop_settings.yml +86 -0
  12. data/.rubocop_todo.yml +709 -0
  13. data/CHANGELOG.md +373 -181
  14. data/CONTRIBUTING.md +141 -0
  15. data/Gemfile +1 -1
  16. data/Jenkinsfile +27 -29
  17. data/LICENSE +202 -0
  18. data/README.md +34 -117
  19. data/SECURITY.md +42 -0
  20. data/bin/parse-changelog.sh +12 -0
  21. data/ci/codeclimate.dockerfile +6 -0
  22. data/conjur-api.gemspec +4 -1
  23. data/docker-compose.yml +2 -0
  24. data/features/authenticators.feature +33 -0
  25. data/features/host.feature +39 -9
  26. data/features/step_definitions/api_steps.rb +14 -3
  27. data/features/step_definitions/policy_steps.rb +40 -0
  28. data/features/support/env.rb +2 -0
  29. data/features/update_password.feature +2 -2
  30. data/features/user.feature +47 -6
  31. data/features_v4/support/env.rb +2 -0
  32. data/lib/conjur-api/version.rb +2 -2
  33. data/lib/conjur/acts_as_role.rb +15 -19
  34. data/lib/conjur/acts_as_user.rb +5 -1
  35. data/lib/conjur/api.rb +1 -18
  36. data/lib/conjur/api/authenticators.rb +35 -0
  37. data/lib/conjur/api/authn.rb +3 -3
  38. data/lib/conjur/api/host_factories.rb +20 -19
  39. data/lib/conjur/api/resources.rb +17 -21
  40. data/lib/conjur/api/router/v4.rb +80 -23
  41. data/lib/conjur/api/router/v5.rb +117 -23
  42. data/lib/conjur/base.rb +19 -5
  43. data/lib/conjur/base_object.rb +31 -26
  44. data/lib/conjur/build_object.rb +13 -20
  45. data/lib/conjur/cert_utils.rb +14 -0
  46. data/lib/conjur/configuration.rb +46 -24
  47. data/lib/conjur/id.rb +22 -19
  48. data/lib/conjur/role_grant.rb +13 -18
  49. data/spec/api/host_factories_spec.rb +34 -0
  50. data/spec/api_spec.rb +55 -6
  51. data/spec/base_object_spec.rb +13 -0
  52. data/spec/cert_utils_spec.rb +92 -0
  53. data/spec/configuration_spec.rb +25 -3
  54. data/spec/id_spec.rb +29 -0
  55. data/spec/spec_helper.rb +4 -1
  56. data/spec/ssl_spec.rb +50 -26
  57. data/spec/uri_escape_spec.rb +14 -2
  58. data/test.sh +23 -1
  59. metadata +31 -14
  60. data/LICENSE.md +0 -195
  61. data/lib/conjur/cast.rb +0 -41
  62. data/spec/cast_spec.rb +0 -21
  63. data/spec/vendor/rest_client_spec.rb +0 -41
data/lib/conjur/base.rb CHANGED
@@ -123,19 +123,21 @@ module Conjur
123
123
  #
124
124
  # @return [String] the api key, or nil if this instance was created from a token.
125
125
  attr_reader :api_key
126
-
126
+
127
127
  #@!attribute [r] remote_ip
128
128
  # An optional IP address to be recorded in the audit record for any actions performed by this API instance.
129
129
  attr_reader :remote_ip
130
130
 
131
131
  # The name of the user as which this api instance is authenticated. This is available whether the api
132
- # instance was created from credentials or an authentication token.
132
+ # instance was created from credentials or an authentication token. If the instance was created from
133
+ # credentials, we will use that value directly otherwise we will attempt to extract the username from
134
+ # the token (either the old-style data field or the new-style JWT `sub` field).
133
135
  #
134
136
  # @return [String] the login of the current user.
135
137
  def username
136
- @username || token['data']
138
+ @username || token['data'] || jwt_username(token)
137
139
  end
138
-
140
+
139
141
  # @api private
140
142
  # used to delegate to host providing subclasses.
141
143
  # @return [String] the host
@@ -213,7 +215,7 @@ module Conjur
213
215
  @account = account
214
216
  @username = username
215
217
  @api_key = api_key
216
-
218
+
217
219
  update_token_born
218
220
  end
219
221
 
@@ -323,6 +325,18 @@ module Conjur
323
325
 
324
326
  private
325
327
 
328
+ # Tries to get the username (subject) from a JWT API token by examining
329
+ # its content.
330
+ #
331
+ # @return [String] of the 'sub' payload field from the JWT if present,
332
+ # otherwise return nil
333
+ def jwt_username raw_token
334
+ return nil unless raw_token
335
+ return nil unless raw_token.include? 'payload'
336
+
337
+ JSON.parse(Base64.strict_decode64(raw_token["payload"]))["sub"]
338
+ end
339
+
326
340
  # Tries to refresh the token if possible.
327
341
  #
328
342
  # @return [Hash, false] false if the token couldn't be refreshed due to
@@ -1,37 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2013-2018 CyberArk Ltd.
1
4
  #
2
- # Copyright 2013-2017 Conjur Inc
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
- # this software and associated documentation files (the "Software"), to deal in
6
- # the Software without restriction, including without limitation the rights to
7
- # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
- # the Software, and to permit persons to whom the Software is furnished to do so,
9
- # subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in all
12
- # copies or substantial portions of the Software.
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
13
8
  #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
- # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
- # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
20
10
  #
21
- require 'conjur/cast'
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
22
16
 
23
17
  module Conjur
24
18
  class BaseObject
25
- include Cast
26
19
  include QueryString
27
20
  include LogSource
28
21
  include BuildObject
29
22
  include Routing
30
-
23
+
31
24
  attr_reader :id, :credentials
32
-
25
+
33
26
  def initialize id, credentials
34
- @id = cast_to_id(id)
27
+ @id = Id.new id
35
28
  @credentials = credentials
36
29
  end
37
30
 
@@ -41,12 +34,24 @@ module Conjur
41
34
  }
42
35
  end
43
36
 
44
- def account; id.account; end
45
- def kind; id.kind; end
46
- def identifier; id.identifier; end
47
-
37
+ def account
38
+ id.account
39
+ end
40
+
41
+ def kind
42
+ id.kind
43
+ end
44
+
45
+ def identifier
46
+ id.identifier
47
+ end
48
+
48
49
  def username
49
50
  credentials[:username] or raise "No username found in credentials"
50
51
  end
52
+
53
+ def inspect
54
+ "<#{self.class.name} id='#{id.to_s}'>"
55
+ end
51
56
  end
52
57
  end
@@ -1,37 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2013-2018 CyberArk Ltd.
1
4
  #
2
- # Copyright 2013-2017 Conjur Inc
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
- # this software and associated documentation files (the "Software"), to deal in
6
- # the Software without restriction, including without limitation the rights to
7
- # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
- # the Software, and to permit persons to whom the Software is furnished to do so,
9
- # subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in all
12
- # copies or substantial portions of the Software.
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
13
8
  #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
- # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
- # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
20
10
  #
21
- require 'conjur/cast'
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
22
16
 
23
17
  module Conjur
24
18
  module BuildObject
25
19
  def self.included base
26
20
  base.module_eval do
27
- extend Cast
28
21
  extend ClassMethods
29
22
  end
30
23
  end
31
24
 
32
25
  module ClassMethods
33
26
  def build_object id, credentials, default_class:
34
- id = cast_to_id(id)
27
+ id = Id.new id
35
28
  class_name = id.kind.classify.to_sym
36
29
  find_class(class_name, default_class)
37
30
  .new(id, credentials)
@@ -44,6 +44,20 @@ module Conjur
44
44
  end
45
45
  end
46
46
  end
47
+
48
+ # Add a certificate to a given store. If the certificate has more than
49
+ # one certificate in its chain, it will be parsed and added to the store
50
+ # one by one. This is done because `OpenSSL::X509::Store.new.add_cert`
51
+ # adds only the intermediate certificate to the store.
52
+ def add_chained_cert store, chained_cert
53
+ parse_certs(chained_cert).each do |cert|
54
+ begin
55
+ store.add_cert cert
56
+ rescue OpenSSL::X509::StoreError => ex
57
+ raise unless ex.message == 'cert already in hash table'
58
+ end
59
+ end
60
+ end
47
61
  end
48
62
  end
49
63
  end
@@ -24,7 +24,6 @@ require 'set'
24
24
  require 'conjur/cert_utils'
25
25
 
26
26
  module Conjur
27
-
28
27
  class << self
29
28
  # Saves the current thread local {Conjur::Configuration},
30
29
  # sets the thread local {Conjur::Configuration} to `config`, yields to the block, and ensures that
@@ -68,7 +67,7 @@ module Conjur
68
67
  ensure
69
68
  Thread.current[:conjur_configuration] = oldvalue
70
69
  end
71
-
70
+
72
71
  # Gets the current thread-local or global configuration.
73
72
  #
74
73
  # The thread-local Conjur configuration can only be set using the {Conjur.with_configuration}
@@ -79,7 +78,7 @@ module Conjur
79
78
  def configuration
80
79
  Thread.current[:conjur_configuration] || (@config ||= Configuration.new)
81
80
  end
82
-
81
+
83
82
  # Sets the global configuration.
84
83
  #
85
84
  # This method *has no effect* on the thread local configuration. Use {Conjur.with_configuration} instead if
@@ -191,25 +190,25 @@ module Conjur
191
190
  @supplied = options.dup
192
191
  @computed = Hash.new
193
192
  end
194
-
193
+
195
194
  class << self
196
195
  # @api private
197
196
  def accepted_options
198
197
  require 'set'
199
198
  @options ||= Set.new
200
199
  end
201
-
200
+
202
201
  # @param [Symbol] name
203
202
  # @param [Hash] options
204
- # @option options [Boolean] :boolean (false) whether this option should have a '?' accessor
203
+ # @option options [Boolean] :boolean (false) whether this option should have a '?' accessor
205
204
  # @option options [Boolean, String] :env Environment variable for this option. Set to false
206
205
  # to disallow environment based configuration. Default is CONJUR_<OPTION_NAME>.
207
206
  # @option options [Proc, *] :default Default value or proc to provide it
208
207
  # @option options [Boolean] :required (false) when true, raise an exception if the option is
209
208
  # not set
210
- # @option options [Proc, #to_proc] :convert proc-ish to convert environment
209
+ # @option options [Proc, #to_proc] :convert proc-ish to convert environment
211
210
  # values to appropriate types
212
- # @param [Proc] def_proc block to provide default values
211
+ # @param [Proc] def_proc block to provide default values
213
212
  # @api private
214
213
  def add_option name, options = {}, &def_proc
215
214
  accepted_options << name
@@ -217,7 +216,7 @@ module Conjur
217
216
  env_var = options[:env] || "CONJUR_#{name.to_s.upcase}"
218
217
  def_val = options[:default]
219
218
  opt_name = name
220
-
219
+
221
220
  def_proc ||= if def_val.respond_to?(:call)
222
221
  def_val
223
222
  elsif options[:required]
@@ -225,10 +224,10 @@ module Conjur
225
224
  else
226
225
  proc { def_val }
227
226
  end
228
-
227
+
229
228
  convert = options[:convert] || ->(x){ x }
230
229
  # Allow a Symbol, for example
231
- convert = convert.to_proc if convert.respond_to?(:to_proc)
230
+ convert = convert.to_proc if convert.respond_to?(:to_proc)
232
231
 
233
232
  define_method("#{name}=") do |value|
234
233
  set name, value
@@ -237,7 +236,7 @@ module Conjur
237
236
  define_method("#{name}_env_var") do
238
237
  allow_env ? env_var : nil
239
238
  end
240
-
239
+
241
240
  define_method(name) do
242
241
  value = computed[name]
243
242
  return value unless value.nil?
@@ -246,7 +245,7 @@ module Conjur
246
245
  supplied[name]
247
246
  elsif allow_env && ENV.member?(env_var)
248
247
  instance_exec(ENV[env_var], &convert)
249
- else
248
+ else
250
249
  instance_eval(&def_proc)
251
250
  end.tap do |value|
252
251
  computed[name] = value
@@ -256,7 +255,7 @@ module Conjur
256
255
  alias_method("#{name}?", name) if options[:boolean]
257
256
  end
258
257
  end
259
-
258
+
260
259
  # Return a copy of this {Conjur::Configuration} instance, optionally
261
260
  # updating the copy with options from the `override_options` hash.
262
261
  #
@@ -290,8 +289,8 @@ module Conjur
290
289
  #
291
290
  # The url for the {http://developer.conjur.net/reference/services/authentication Conjur authentication service}.
292
291
  #
293
- # By default, this will be built from the +appliance_url+. To use a custom authenticator,
294
- # set this option in code or set `CONJUR_AUTHN_URL`.
292
+ # By default, this will be built from the +appliance_url+. To use a custom authenticator,
293
+ # set this option in code or set `CONJUR_AUTHN_URL`.
295
294
  #
296
295
  #
297
296
  # @return [String] the authentication service url
@@ -369,10 +368,30 @@ module Conjur
369
368
  # @see cert_file
370
369
  add_option :ssl_certificate
371
370
 
371
+ # @!attribute rest_client_options
372
+ #
373
+ # Custom options for the underlying RestClient Requests. This defaults to:
374
+ # ```
375
+ # {
376
+ # ssl_cert_store: OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
377
+ # }
378
+ # ``
379
+ #
380
+ # The `ssl_cert_store` value aligns with the default certificate store used by
381
+ # {#apply_cert_config!}.
382
+ #
383
+ # NOTE: When setting the value of rest_client_options the defaults are not retained,
384
+ # you must manually set them on the value you provide.
385
+ add_option :rest_client_options do
386
+ {
387
+ ssl_cert_store: OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
388
+ }
389
+ end
390
+
372
391
  # @!attribute version
373
392
  #
374
393
  # Selects the major API version of the Conjur server. With this setting, the API
375
- # will use the routing scheme for API version `4` or `5`.
394
+ # will use the routing scheme for API version `4` or `5`.
376
395
  #
377
396
  # Methods which are not available in the selected version will raise NoMethodError.
378
397
  add_option :version, default: 5
@@ -383,6 +402,12 @@ module Conjur
383
402
  # This is only available when the API client is running on the Conjur server.
384
403
  add_option :authn_local_socket, default: "/run/authn-local/.socket"
385
404
 
405
+ # Create rest_client_options by merging the input with the
406
+ # rest_client_options present on the configuration object.
407
+ def create_rest_client_options options
408
+ rest_client_options.merge(options || {})
409
+ end
410
+
386
411
  # Calls a major-version-specific function.
387
412
  def version_logic v4_logic, v5_logic
388
413
  case version.to_s
@@ -398,17 +423,14 @@ module Conjur
398
423
  # Add the certificate configured by the {#ssl_certificate} and {#cert_file} options to the certificate
399
424
  # store used by Conjur clients.
400
425
  #
426
+ # NOTE: If you specify a non-default `store` value, you must manually set the
427
+ # `ssl_cert_store` value on {#rest_client_options} to the same value.
428
+ #
401
429
  # @param [OpenSSL::X509::Store] store the certificate store that the certificate will be installed in.
402
430
  # @return [Boolean] whether a certificate was added to the store.
403
431
  def apply_cert_config! store=OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
404
432
  if ssl_certificate
405
- CertUtils.parse_certs(ssl_certificate).each do |cert|
406
- begin
407
- store.add_cert cert
408
- rescue OpenSSL::X509::StoreError => ex
409
- raise unless ex.message == 'cert already in hash table'
410
- end
411
- end
433
+ CertUtils.add_chained_cert(store, ssl_certificate)
412
434
  elsif cert_file
413
435
  ensure_cert_readable!(cert_file)
414
436
  store.add_file cert_file
data/lib/conjur/id.rb CHANGED
@@ -1,23 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2013-2018 CyberArk Ltd.
1
4
  #
2
- # Copyright 2013-2017 Conjur Inc
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
- # this software and associated documentation files (the "Software"), to deal in
6
- # the Software without restriction, including without limitation the rights to
7
- # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
- # the Software, and to permit persons to whom the Software is furnished to do so,
9
- # subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in all
12
- # copies or substantial portions of the Software.
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
13
8
  #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
- # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
- # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
- # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
20
10
  #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
21
17
  require 'conjur/escape'
22
18
 
23
19
  module Conjur
@@ -28,7 +24,7 @@ module Conjur
28
24
  attr_reader :id
29
25
 
30
26
  def initialize id
31
- @id = id
27
+ @id = Id.normalize id
32
28
  end
33
29
 
34
30
  # The organization account, obtained from the first component of the id.
@@ -56,7 +52,7 @@ module Conjur
56
52
  # Splits the id into 3 components, and then joins them with a forward-slash `/`.
57
53
  def to_url_path
58
54
  id.split(':', 3)
59
- .map(&method(:path_escape))
55
+ .map(&method(:fully_escape))
60
56
  .join('/')
61
57
  end
62
58
 
@@ -64,5 +60,12 @@ module Conjur
64
60
  def to_s
65
61
  id
66
62
  end
63
+
64
+ def self.normalize id
65
+ Array(id).join(':').tap do |id|
66
+ raise ArgumentError, "id must be fully qualified: #{id}" \
67
+ unless id =~ /.*:.*:.*/
68
+ end
69
+ end
67
70
  end
68
71
  end