chamber 2.13.0 → 3.0.0rc1

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