chamber 2.13.0 → 3.0.0rc1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +101 -26
  5. data/lib/chamber.rb +77 -16
  6. data/lib/chamber/adapters/cloud/circle_ci.rb +16 -13
  7. data/lib/chamber/adapters/cloud/heroku.rb +40 -13
  8. data/lib/chamber/binary/circle_ci.rb +28 -14
  9. data/lib/chamber/binary/heroku.rb +34 -14
  10. data/lib/chamber/binary/runner.rb +40 -29
  11. data/lib/chamber/binary/travis.rb +10 -4
  12. data/lib/chamber/commands/base.rb +10 -16
  13. data/lib/chamber/commands/cloud/base.rb +3 -3
  14. data/lib/chamber/commands/cloud/pull.rb +2 -2
  15. data/lib/chamber/commands/cloud/push.rb +7 -7
  16. data/lib/chamber/commands/comparable.rb +2 -2
  17. data/lib/chamber/commands/compare.rb +6 -9
  18. data/lib/chamber/commands/initialize.rb +26 -22
  19. data/lib/chamber/commands/securable.rb +12 -9
  20. data/lib/chamber/commands/secure.rb +2 -2
  21. data/lib/chamber/commands/show.rb +8 -8
  22. data/lib/chamber/commands/sign.rb +2 -2
  23. data/lib/chamber/commands/verify.rb +2 -2
  24. data/lib/chamber/configuration.rb +6 -3
  25. data/lib/chamber/context_resolver.rb +7 -7
  26. data/lib/chamber/encryption_methods/ssl.rb +12 -12
  27. data/lib/chamber/file.rb +20 -15
  28. data/lib/chamber/file_set.rb +18 -8
  29. data/lib/chamber/files/signature.rb +16 -14
  30. data/lib/chamber/filters/decryption_filter.rb +19 -15
  31. data/lib/chamber/filters/encryption_filter.rb +14 -11
  32. data/lib/chamber/filters/environment_filter.rb +21 -20
  33. data/lib/chamber/filters/failed_decryption_filter.rb +9 -6
  34. data/lib/chamber/filters/insecure_filter.rb +4 -5
  35. data/lib/chamber/filters/namespace_filter.rb +13 -9
  36. data/lib/chamber/filters/secure_filter.rb +9 -7
  37. data/lib/chamber/filters/translate_secure_keys_filter.rb +9 -7
  38. data/lib/chamber/instance.rb +48 -31
  39. data/lib/chamber/key_pair.rb +7 -7
  40. data/lib/chamber/keys/base.rb +13 -13
  41. data/lib/chamber/keys/decryption.rb +3 -3
  42. data/lib/chamber/keys/encryption.rb +3 -3
  43. data/lib/chamber/namespace_set.rb +2 -4
  44. data/lib/chamber/refinements/array.rb +20 -0
  45. data/lib/chamber/refinements/deep_dup.rb +58 -0
  46. data/lib/chamber/refinements/enumerable.rb +36 -0
  47. data/lib/chamber/refinements/hash.rb +51 -0
  48. data/lib/chamber/settings.rb +81 -66
  49. data/lib/chamber/types/secured.rb +21 -23
  50. data/lib/chamber/version.rb +1 -1
  51. data/templates/settings.yml +2 -0
  52. metadata +44 -51
  53. metadata.gz.sig +0 -0
  54. data/lib/chamber/core_ext/hash.rb +0 -15
@@ -2,29 +2,31 @@
2
2
 
3
3
  require 'openssl'
4
4
  require 'base64'
5
- require 'hashie/mash'
6
5
  require 'yaml'
7
6
  require 'chamber/encryption_methods/public_key'
8
7
  require 'chamber/encryption_methods/ssl'
9
8
  require 'chamber/encryption_methods/none'
10
9
  require 'chamber/errors/decryption_failure'
10
+ require 'chamber/refinements/deep_dup'
11
11
 
12
12
  module Chamber
13
13
  module Filters
14
14
  class DecryptionFilter
15
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+/]{342}==\z}.freeze
15
+ using ::Chamber::Refinements::DeepDup
16
+
17
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
16
18
  LARGE_DATA_STRING_PATTERN = %r{
17
19
  \A # Beginning of String
18
20
  (
19
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Key
21
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Key
20
22
  )
21
23
  \# # Separator
22
24
  (
23
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded IV
25
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded IV
24
26
  )
25
27
  \# # Separator
26
28
  (
27
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Data
29
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Data
28
30
  )
29
31
  \z # End of String
30
32
  }x.freeze
@@ -33,20 +35,20 @@ class DecryptionFilter
33
35
  :secure_key_token
34
36
  attr_reader :decryption_keys
35
37
 
36
- def initialize(options = {})
37
- self.decryption_keys = options.fetch(:decryption_keys, {}) || {}
38
- self.data = options.fetch(:data).dup
39
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
38
+ def initialize(data:, secure_key_prefix:, decryption_keys: {}, **_args)
39
+ self.decryption_keys = decryption_keys || {}
40
+ self.data = data.deep_dup
41
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
40
42
  end
41
43
 
42
- def self.execute(options = {})
43
- new(options).__send__(:execute)
44
+ def self.execute(**args)
45
+ new(**args).__send__(:execute)
44
46
  end
45
47
 
46
48
  protected
47
49
 
48
50
  def execute(raw_data = data)
49
- settings = Hashie::Mash.new
51
+ settings = {}
50
52
 
51
53
  raw_data.each_pair do |key, value|
52
54
  settings[key] = if value.respond_to? :each_pair
@@ -79,9 +81,11 @@ class DecryptionFilter
79
81
  method = decryption_method(value)
80
82
 
81
83
  decryption_keys.each do |decryption_key|
82
- return method.decrypt(key, value, decryption_key)
83
- rescue OpenSSL::PKey::RSAError
84
- next
84
+ begin
85
+ return method.decrypt(key, value, decryption_key)
86
+ rescue OpenSSL::PKey::RSAError
87
+ next
88
+ end
85
89
  end
86
90
 
87
91
  value
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openssl'
4
- require 'hashie/mash'
5
4
  require 'yaml'
6
5
  require 'chamber/encryption_methods/public_key'
7
6
  require 'chamber/encryption_methods/ssl'
8
7
  require 'chamber/encryption_methods/none'
8
+ require 'chamber/refinements/deep_dup'
9
9
 
10
10
  module Chamber
11
11
  module Filters
12
12
  class EncryptionFilter
13
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+\/]{342}==\z}.freeze
14
- BASE64_SUBSTRING_PATTERN = %r{[A-Za-z0-9\+\/#]*\={0,2}}.freeze
13
+ using ::Chamber::Refinements::DeepDup
14
+
15
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
16
+ BASE64_SUBSTRING_PATTERN = %r{[A-Za-z0-9+/#]*={0,2}}.freeze
15
17
  LARGE_DATA_STRING_PATTERN = /
16
18
  \A
17
19
  (#{BASE64_SUBSTRING_PATTERN})
@@ -26,20 +28,20 @@ class EncryptionFilter
26
28
  :secure_key_token
27
29
  attr_reader :encryption_keys
28
30
 
29
- def initialize(options = {})
30
- self.encryption_keys = options.fetch(:encryption_keys, {}) || {}
31
- self.data = options.fetch(:data).dup
32
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
31
+ def initialize(data:, secure_key_prefix:, encryption_keys: {}, **_args)
32
+ self.encryption_keys = encryption_keys || {}
33
+ self.data = data.deep_dup
34
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
33
35
  end
34
36
 
35
- def self.execute(options = {})
36
- new(options).__send__(:execute)
37
+ def self.execute(**args)
38
+ new(**args).__send__(:execute)
37
39
  end
38
40
 
39
41
  protected
40
42
 
41
43
  def execute(raw_data = data, namespace = nil)
42
- raw_data.each_with_object(Hashie::Mash.new) do |(key, value), settings|
44
+ raw_data.each_with_object({}) do |(key, value), settings|
43
45
  settings[key] = if value.respond_to? :each_pair
44
46
  execute(value, namespace || key)
45
47
  elsif key.match(secure_key_token)
@@ -67,7 +69,8 @@ class EncryptionFilter
67
69
 
68
70
  def encrypt(namespace, key, value)
69
71
  method = encryption_method(value)
70
- encryption_key = encryption_keys[namespace] || encryption_keys[:__default]
72
+ namespace_key = namespace ? namespace.to_sym : nil
73
+ encryption_key = encryption_keys[namespace_key] || encryption_keys[:__default]
71
74
 
72
75
  return value unless encryption_key
73
76
 
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
- require 'hashie/mash'
5
-
6
4
  require 'chamber/errors/environment_conversion'
5
+ require 'chamber/refinements/hash'
6
+ require 'chamber/refinements/deep_dup'
7
7
 
8
8
  module Chamber
9
9
  module Filters
10
10
  class EnvironmentFilter
11
+ using ::Chamber::Refinements::Hash
12
+ using ::Chamber::Refinements::DeepDup
13
+
11
14
  ###
12
15
  # Internal: Allows the existing environment to be injected into the passed in
13
16
  # hash. The hash that is passed in is *not* modified, instead a new hash is
@@ -88,16 +91,16 @@ class EnvironmentFilter
88
91
  # }
89
92
  #
90
93
  #
91
- def self.execute(options = {})
92
- new(options).__send__(:execute)
94
+ def self.execute(**args)
95
+ new(**args).__send__(:execute)
93
96
  end
94
97
 
95
98
  attr_accessor :data,
96
99
  :secure_key_token
97
100
 
98
- def initialize(options = {})
99
- self.data = options.fetch(:data)
100
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
101
+ def initialize(data:, secure_key_prefix:, **_args)
102
+ self.data = data
103
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
101
104
  end
102
105
 
103
106
  protected
@@ -120,26 +123,25 @@ class EnvironmentFilter
120
123
  private
121
124
 
122
125
  def with_environment(settings, parent_keys, hash_block, value_block)
123
- environment_hash = Hashie::Mash.new
126
+ environment_hash = {}
124
127
 
125
128
  settings.each_pair do |key, value|
126
129
  environment_key = key.to_s.gsub(secure_key_token, '')
127
- environment_keys = parent_keys.dup.push(environment_key)
130
+ environment_keys = parent_keys.deep_dup.push(environment_key)
128
131
 
129
132
  if value.respond_to? :each_pair
130
- environment_hash.merge!(hash_block.call(key, value, environment_keys))
133
+ environment_hash.deep_merge!(hash_block.call(key, value, environment_keys))
131
134
  else
132
135
  environment_key = environment_keys.join('_').upcase
133
136
 
134
- environment_hash.merge!(value_block.call(key, value, environment_key))
137
+ environment_hash.deep_merge!(value_block.call(key, value, environment_key))
135
138
  end
136
139
  end
137
140
 
138
141
  environment_hash
139
142
  end
140
143
 
141
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
142
- def convert_environment_value(environment_key, environment_value, settings_value)
144
+ def convert_environment_value(environment_key, environment_value, settings_value) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
143
145
  return settings_value unless environment_value
144
146
  return if %w{___nil___ ___null___}.include?(environment_value)
145
147
 
@@ -163,22 +165,21 @@ class EnvironmentFilter
163
165
  fail ArgumentError, "Invalid value for Array: #{environment_value}"
164
166
  end
165
167
  end
166
- when 'Integer'
168
+ when 'Fixnum', 'Integer'
167
169
  Integer(environment_value)
168
170
  else
169
171
  environment_value
170
172
  end
171
173
  rescue ArgumentError
172
- raise Chamber::Errors::EnvironmentConversion, <<~HEREDOC
173
- We attempted to convert '#{environment_key}' from '#{environment_value}' to a '#{settings_value.class.name}'.
174
+ raise Chamber::Errors::EnvironmentConversion, <<-HEREDOC
175
+ We attempted to convert '#{environment_key}' from '#{environment_value}' to a '#{settings_value.class.name}'.
174
176
 
175
- Unfortunately, this did not go as planned. Please either verify that your value is convertable
176
- or change the original YAML value to be something more generic (like a String).
177
+ Unfortunately, this did not go as planned. Please either verify that your value is convertable
178
+ or change the original YAML value to be something more generic (like a String).
177
179
 
178
- For more information, see https://github.com/thekompanee/chamber/wiki/Environment-Variable-Coercions
180
+ For more information, see https://github.com/thekompanee/chamber/wiki/Environment-Variable-Coercions
179
181
  HEREDOC
180
182
  end
181
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
182
183
  end
183
184
  end
184
185
  end
@@ -1,22 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'chamber/errors/decryption_failure'
4
+ require 'chamber/refinements/deep_dup'
4
5
 
5
6
  module Chamber
6
7
  module Filters
7
8
  class FailedDecryptionFilter
8
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+/]{342}==\z}.freeze
9
+ using ::Chamber::Refinements::DeepDup
9
10
 
10
- def self.execute(options = {})
11
- new(options).__send__(:execute)
11
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
12
+
13
+ def self.execute(**args)
14
+ new(**args).__send__(:execute)
12
15
  end
13
16
 
14
17
  attr_accessor :data,
15
18
  :secure_key_token
16
19
 
17
- def initialize(options = {})
18
- self.data = options.fetch(:data).dup
19
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
20
+ def initialize(data:, secure_key_prefix:, **_args)
21
+ self.data = data.deep_dup
22
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
20
23
  end
21
24
 
22
25
  protected
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashie/mash'
4
3
  require 'chamber/filters/secure_filter'
5
4
 
6
5
  module Chamber
7
6
  module Filters
8
7
  class InsecureFilter < SecureFilter
9
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+\/]{342}==\z}.freeze
10
- BASE64_SUBSTRING_PATTERN = %r{[A-Za-z0-9\+\/#]*\={0,2}}.freeze
8
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
9
+ BASE64_SUBSTRING_PATTERN = %r{[A-Za-z0-9+/#]*={0,2}}.freeze
11
10
  LARGE_DATA_STRING_PATTERN = /
12
11
  \A
13
12
  (#{BASE64_SUBSTRING_PATTERN})
@@ -20,9 +19,9 @@ class InsecureFilter < SecureFilter
20
19
 
21
20
  protected
22
21
 
23
- def execute(raw_data = data)
22
+ def execute(raw_data = data) # rubocop:disable Metrics/CyclomaticComplexity
24
23
  securable_settings = super
25
- settings = Hashie::Mash.new
24
+ settings = {}
26
25
 
27
26
  securable_settings.each_pair do |key, value|
28
27
  value = if value.respond_to? :each_pair
@@ -1,31 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashie/mash'
3
+ require 'chamber/refinements/hash'
4
+ require 'chamber/refinements/deep_dup'
4
5
 
5
6
  module Chamber
6
7
  module Filters
7
8
  class NamespaceFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
9
+ using ::Chamber::Refinements::Hash
10
+ using ::Chamber::Refinements::DeepDup
11
+
12
+ def self.execute(**args)
13
+ new(**args).__send__(:execute)
10
14
  end
11
15
 
12
16
  attr_accessor :data,
13
17
  :namespaces
14
18
 
15
- def initialize(options = {})
16
- self.data = Hashie::Mash.new(options.fetch(:data))
17
- self.namespaces = options.fetch(:namespaces)
19
+ def initialize(data:, namespaces:, **_args)
20
+ self.data = data.deep_dup
21
+ self.namespaces = namespaces
18
22
  end
19
23
 
20
24
  protected
21
25
 
22
26
  def execute
23
27
  if data_is_namespaced?
24
- namespaces.each_with_object(Hashie::Mash.new) do |namespace, filtered_data|
25
- filtered_data.merge!(data[namespace]) if data[namespace]
28
+ namespaces.each_with_object({}) do |namespace, filtered_data|
29
+ filtered_data.deep_merge!(data[namespace]) if data[namespace]
26
30
  end
27
31
  else
28
- Hashie::Mash.new(data)
32
+ data
29
33
  end
30
34
  end
31
35
 
@@ -1,26 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashie/mash'
3
+ require 'chamber/refinements/deep_dup'
4
4
 
5
5
  module Chamber
6
6
  module Filters
7
7
  class SecureFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
8
+ using ::Chamber::Refinements::DeepDup
9
+
10
+ def self.execute(**args)
11
+ new(**args).__send__(:execute)
10
12
  end
11
13
 
12
14
  attr_accessor :data,
13
15
  :secure_key_token
14
16
 
15
- def initialize(options = {})
16
- self.data = Hashie::Mash.new(options.fetch(:data))
17
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
17
+ def initialize(data:, secure_key_prefix:, **_args)
18
+ self.data = data.deep_dup
19
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
18
20
  end
19
21
 
20
22
  protected
21
23
 
22
24
  def execute(raw_data = data)
23
- settings = Hashie::Mash.new
25
+ settings = {}
24
26
 
25
27
  raw_data.each_pair do |key, value|
26
28
  secure_value = if value.respond_to? :each_pair
@@ -1,26 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashie/mash'
3
+ require 'chamber/refinements/deep_dup'
4
4
 
5
5
  module Chamber
6
6
  module Filters
7
7
  class TranslateSecureKeysFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
8
+ using ::Chamber::Refinements::DeepDup
9
+
10
+ def self.execute(**args)
11
+ new(**args).__send__(:execute)
10
12
  end
11
13
 
12
14
  attr_accessor :data,
13
15
  :secure_key_token
14
16
 
15
- def initialize(options = {})
16
- self.data = options.fetch(:data).dup
17
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
17
+ def initialize(data:, secure_key_prefix:, **_args)
18
+ self.data = data.deep_dup
19
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
18
20
  end
19
21
 
20
22
  protected
21
23
 
22
24
  def execute(raw_data = data)
23
- settings = Hashie::Mash.new
25
+ settings = {}
24
26
 
25
27
  raw_data.each_pair do |key, value|
26
28
  value = execute(value) if value.respond_to? :each_pair
@@ -3,21 +3,36 @@
3
3
  require 'chamber/configuration'
4
4
  require 'chamber/file_set'
5
5
  require 'chamber/settings'
6
+ require 'chamber/refinements/hash'
6
7
 
7
8
  module Chamber
8
9
  class Instance
10
+ using ::Chamber::Refinements::Hash
11
+
9
12
  attr_accessor :configuration,
10
13
  :files
11
14
 
12
- def initialize(options = {})
13
- self.configuration = Configuration.new options
14
- self.files = FileSet.new configuration.to_hash
15
+ def initialize(**args)
16
+ self.configuration = Configuration.new(**args)
17
+ self.files = FileSet.new(**configuration.to_hash)
15
18
  end
16
19
 
17
20
  def settings
18
21
  @settings ||= files.to_settings { |settings| @settings = settings }
19
22
  end
20
23
 
24
+ def [](key)
25
+ settings.[](key)
26
+ end
27
+
28
+ def dig!(*args)
29
+ settings.dig!(*args)
30
+ end
31
+
32
+ def dig(*args)
33
+ settings.dig(*args)
34
+ end
35
+
21
36
  def filenames
22
37
  files.filenames
23
38
  end
@@ -34,26 +49,42 @@ class Instance
34
49
  files.verify
35
50
  end
36
51
 
37
- def encrypt(data, options = {})
38
- config = configuration.to_hash.merge(options)
52
+ def to_environment
53
+ settings.to_environment
54
+ end
55
+
56
+ def to_s(**args)
57
+ settings.to_s(**args)
58
+ end
59
+
60
+ def to_hash
61
+ settings.to_hash
62
+ end
63
+
64
+ def namespaces
65
+ settings.namespaces
66
+ end
39
67
 
40
- Settings.
41
- new(
42
- config.merge(
68
+ def encrypt(data, **args)
69
+ config = configuration.to_hash.deep_merge(**args)
70
+
71
+ Settings
72
+ .new(
73
+ **config.deep_merge(
43
74
  settings: data,
44
75
  pre_filters: [Filters::EncryptionFilter],
45
76
  post_filters: [],
46
77
  ),
47
- ).
48
- to_hash
78
+ )
79
+ .to_hash
49
80
  end
50
81
 
51
- def decrypt(data, options = {})
52
- config = configuration.to_hash.merge(options)
82
+ def decrypt(data, **args)
83
+ config = configuration.to_hash.deep_merge(**args)
53
84
 
54
- Settings.
55
- new(
56
- config.merge(
85
+ Settings
86
+ .new(
87
+ **config.deep_merge(
57
88
  settings: data,
58
89
  pre_filters: [Filters::NamespaceFilter],
59
90
  post_filters: [
@@ -61,22 +92,8 @@ class Instance
61
92
  Filters::FailedDecryptionFilter,
62
93
  ],
63
94
  ),
64
- ).
65
- to_hash
66
- end
67
-
68
- def to_s(options = {})
69
- settings.to_s(options)
70
- end
71
-
72
- def method_missing(name, *args)
73
- return settings.public_send(name, *args) if settings.respond_to?(name)
74
-
75
- super
76
- end
77
-
78
- def respond_to_missing?(name, include_private = false)
79
- settings.respond_to?(name, include_private)
95
+ )
96
+ .to_hash
80
97
  end
81
98
  end
82
99
  end