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
@@ -9,10 +9,10 @@ class KeyPair
9
9
  :namespace,
10
10
  :passphrase
11
11
 
12
- def initialize(options = {})
13
- self.namespace = options[:namespace]
14
- self.passphrase = options.fetch(:passphrase, SecureRandom.uuid)
15
- self.key_file_path = Pathname.new(options.fetch(:key_file_path))
12
+ def initialize(key_file_path:, namespace: nil, passphrase: ::SecureRandom.uuid)
13
+ self.namespace = namespace
14
+ self.passphrase = passphrase
15
+ self.key_file_path = Pathname.new(key_file_path)
16
16
  end
17
17
 
18
18
  def encrypted_private_key_passphrase_filepath
@@ -78,9 +78,9 @@ class KeyPair
78
78
  @base_key_filename ||= [
79
79
  '.chamber',
80
80
  namespace ? namespace.tr('-.', '') : nil,
81
- ].
82
- compact.
83
- join('.')
81
+ ]
82
+ .compact
83
+ .join('.')
84
84
  end
85
85
  end
86
86
  end
@@ -3,18 +3,18 @@
3
3
  module Chamber
4
4
  module Keys
5
5
  class Base
6
- def self.resolve(*args)
7
- new(*args).resolve
6
+ def self.resolve(**args)
7
+ new(**args).resolve
8
8
  end
9
9
 
10
10
  attr_accessor :rootpath
11
11
  attr_reader :filenames,
12
12
  :namespaces
13
13
 
14
- def initialize(options = {})
15
- self.rootpath = Pathname.new(options.fetch(:rootpath))
16
- self.namespaces = options.fetch(:namespaces)
17
- self.filenames = options[:filenames]
14
+ def initialize(rootpath:, namespaces:, filenames: nil)
15
+ self.rootpath = Pathname.new(rootpath)
16
+ self.namespaces = namespaces
17
+ self.filenames = filenames
18
18
  end
19
19
 
20
20
  def resolve
@@ -41,9 +41,9 @@ class Base
41
41
 
42
42
  # rubocop:disable Performance/ChainArrayAllocation
43
43
  def filenames=(other)
44
- @filenames = Array(other).
45
- map { |o| Pathname.new(o) }.
46
- compact
44
+ @filenames = Array(other)
45
+ .map { |o| Pathname.new(o) }
46
+ .compact
47
47
  end
48
48
  # rubocop:enable Performance/ChainArrayAllocation
49
49
 
@@ -52,10 +52,10 @@ class Base
52
52
  end
53
53
 
54
54
  def namespace_from_path(path)
55
- path.
56
- basename.
57
- to_s.
58
- match(self.class::NAMESPACE_PATTERN) { |m| m[1].upcase }
55
+ path
56
+ .basename
57
+ .to_s
58
+ .match(self.class::NAMESPACE_PATTERN) { |m| m[1].upcase }
59
59
  end
60
60
 
61
61
  def namespace_to_key_path(namespace)
@@ -22,9 +22,9 @@ class Decryption < Chamber::Keys::Base
22
22
  'CHAMBER',
23
23
  namespace_from_path(path),
24
24
  'KEY',
25
- ].
26
- compact.
27
- join('_')
25
+ ]
26
+ .compact
27
+ .join('_')
28
28
  end
29
29
 
30
30
  def key_filename_extension
@@ -22,9 +22,9 @@ class Encryption < Chamber::Keys::Base
22
22
  'CHAMBER',
23
23
  namespace_from_path(path),
24
24
  'PUBLIC_KEY',
25
- ].
26
- compact.
27
- join('_')
25
+ ]
26
+ .compact
27
+ .join('_')
28
28
  end
29
29
 
30
30
  def key_filename_extension
@@ -71,10 +71,8 @@ class NamespaceSet
71
71
  # Internal: Iterates over each namespace value and allows it to be used in
72
72
  # a block.
73
73
  #
74
- def each
75
- namespaces.each do |namespace|
76
- yield namespace
77
- end
74
+ def each(&block)
75
+ namespaces.each(&block)
78
76
  end
79
77
 
80
78
  ###
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspectacular'
4
+ require 'chamber/refinements/enumerable'
5
+
6
+ module Chamber
7
+ module Refinements
8
+ module Array
9
+ refine ::Array do
10
+ def deep_transform_keys(&block)
11
+ Refinements::Enumerable.deep_transform_keys(self, &block)
12
+ end
13
+
14
+ def deep_transform_values(&block)
15
+ Refinements::Enumerable.deep_transform_values(nil, self, &block)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Refinements
5
+ module DeepDup
6
+ refine ::Array do
7
+ unless method_defined?(:deep_dup)
8
+ def deep_dup
9
+ map do |i|
10
+ if i.respond_to?(:deep_dup)
11
+ i.deep_dup
12
+ else
13
+ begin
14
+ i.dup
15
+ rescue ::TypeError
16
+ # Hack for < Ruby 2.4 since FalseClass, TrueClass, Fixnum, etc can't be
17
+ # dupped
18
+ i
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ refine ::Object do
27
+ unless method_defined?(:deep_dup)
28
+ def deep_dup
29
+ begin
30
+ dup
31
+ rescue ::TypeError
32
+ # Hack for < Ruby 2.4 since FalseClass, TrueClass, Fixnum, etc can't be
33
+ # dupped
34
+ self
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ refine ::Hash do
41
+ unless method_defined?(:deep_dup)
42
+ def deep_dup
43
+ dup.tap do |hash|
44
+ each_pair do |key, value|
45
+ if key.frozen? && key.is_a?(::String)
46
+ hash[key] = value.deep_dup
47
+ else
48
+ hash.delete(key)
49
+ hash[key.deep_dup] = value.deep_dup
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Refinements
5
+ class Enumerable
6
+ def self.deep_transform_keys(object, &block)
7
+ case object
8
+ when ::Hash
9
+ object.each_with_object({}) do |(key, value), result|
10
+ result[yield(key)] = deep_transform_keys(value, &block)
11
+ end
12
+ when ::Array
13
+ object.map { |e| deep_transform_keys(e, &block) }
14
+ else
15
+ object
16
+ end
17
+ end
18
+
19
+ def self.deep_transform_values(key, value, &block)
20
+ case value
21
+ when ::Hash
22
+ value.each_with_object({}) do |(k, v), memo|
23
+ memo[k] = deep_transform_values(k, v, &block)
24
+ end
25
+ when ::Array
26
+ yield(
27
+ key,
28
+ value.map { |v| deep_transform_values(nil, v, &block) }
29
+ )
30
+ else
31
+ yield(key, value)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspectacular'
4
+ require 'chamber/refinements/hash'
5
+ require 'chamber/refinements/enumerable'
6
+
7
+ module Chamber
8
+ module Refinements
9
+ module Hash
10
+ refine ::Hash do
11
+ def deep_strip!
12
+ each do |key, value|
13
+ if value.respond_to?(:strip)
14
+ self[key] = value.strip
15
+ elsif value.respond_to?(:deep_strip!)
16
+ self[key] = value.deep_strip!
17
+ end
18
+ end
19
+ end
20
+
21
+ def deep_transform_keys(&block)
22
+ Refinements::Enumerable.deep_transform_keys(self, &block)
23
+ end
24
+
25
+ def deep_transform_values(&block)
26
+ Refinements::Enumerable.deep_transform_values(nil, self, &block)
27
+ end
28
+
29
+ unless method_defined?(:deep_merge)
30
+ def deep_merge(other, &block)
31
+ dup.deep_merge!(other, &block)
32
+ end
33
+ end
34
+
35
+ unless method_defined?(:deep_merge!)
36
+ def deep_merge!(other, &block)
37
+ merge!(other) do |key, value_1, value_2|
38
+ if value_1.is_a?(::Hash) && value_2.is_a?(::Hash)
39
+ value_1.deep_merge(value_2, &block)
40
+ elsif block
41
+ yield(key, value_1, value_2)
42
+ else
43
+ value_2
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashie/mash'
4
3
  require 'chamber/namespace_set'
5
4
  require 'chamber/filters/namespace_filter'
6
5
  require 'chamber/filters/encryption_filter'
@@ -10,35 +9,52 @@ require 'chamber/filters/secure_filter'
10
9
  require 'chamber/filters/translate_secure_keys_filter'
11
10
  require 'chamber/filters/insecure_filter'
12
11
  require 'chamber/filters/failed_decryption_filter'
12
+ require 'chamber/refinements/deep_dup'
13
+ require 'chamber/refinements/hash'
13
14
 
14
15
  ###
15
16
  # Internal: Represents the base settings storage needed for Chamber.
16
17
  #
17
18
  module Chamber
18
19
  class Settings
19
- attr_accessor :pre_filters,
20
- :post_filters,
20
+ using ::Chamber::Refinements::Hash
21
+ using ::Chamber::Refinements::DeepDup
22
+
23
+ attr_accessor :decryption_keys,
21
24
  :encryption_keys,
22
- :decryption_keys
25
+ :post_filters,
26
+ :pre_filters,
27
+ :secure_key_prefix
23
28
  attr_reader :namespaces
24
-
25
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/LineLength
26
- def initialize(options = {})
27
- self.namespaces = options[:namespaces] || []
28
- self.raw_data = options[:settings] || {}
29
- self.decryption_keys = options[:decryption_keys] || {}
30
- self.encryption_keys = options[:encryption_keys] || {}
31
- self.pre_filters = options[:pre_filters] || [
32
- Filters::NamespaceFilter,
33
- ]
34
- self.post_filters = options[:post_filters] || [
35
- Filters::DecryptionFilter,
36
- Filters::EnvironmentFilter,
37
- Filters::FailedDecryptionFilter,
38
- Filters::TranslateSecureKeysFilter,
39
- ]
29
+ attr_writer :raw_data
30
+
31
+ # rubocop:disable Metrics/ParameterLists
32
+ def initialize(
33
+ decryption_keys: {},
34
+ encryption_keys: {},
35
+ namespaces: [],
36
+ pre_filters: [
37
+ Filters::NamespaceFilter,
38
+ ],
39
+ post_filters: [
40
+ Filters::DecryptionFilter,
41
+ Filters::EnvironmentFilter,
42
+ Filters::FailedDecryptionFilter,
43
+ Filters::TranslateSecureKeysFilter,
44
+ ],
45
+ secure_key_prefix: '_secure_',
46
+ settings: {},
47
+ **_args
48
+ )
49
+ self.decryption_keys = decryption_keys
50
+ self.encryption_keys = encryption_keys
51
+ self.namespaces = namespaces
52
+ self.post_filters = post_filters
53
+ self.pre_filters = pre_filters
54
+ self.raw_data = settings.deep_dup
55
+ self.secure_key_prefix = secure_key_prefix
40
56
  end
41
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/LineLength
57
+ # rubocop:enable Metrics/ParameterLists
42
58
 
43
59
  ###
44
60
  # Internal: Converts a Settings object into a hash that is compatible as an
@@ -79,15 +95,11 @@ class Settings
79
95
  # } ).to_s
80
96
  # # => 'MY_KEY="my value" MY_OTHER_KEY="my other value"'
81
97
  #
82
- def to_s(options = {})
83
- hierarchical_separator = options[:hierarchical_separator] || '_'
84
- pair_separator = options[:pair_separator] || ' '
85
- value_surrounder = options[:value_surrounder] || '"'
86
- name_value_separator = options[:name_value_separator] || '='
87
-
88
- concatenated_name_hash = to_concatenated_name_hash(hierarchical_separator)
89
-
90
- pairs = concatenated_name_hash.to_a.map do |key, value|
98
+ def to_s(hierarchical_separator: '_',
99
+ pair_separator: ' ',
100
+ value_surrounder: '"',
101
+ name_value_separator: '=')
102
+ pairs = to_concatenated_name_hash(hierarchical_separator).to_a.map do |key, value|
91
103
  "#{key.upcase}#{name_value_separator}#{value_surrounder}#{value}#{value_surrounder}"
92
104
  end
93
105
 
@@ -102,7 +114,7 @@ class Settings
102
114
  # Returns a Hash
103
115
  #
104
116
  def to_hash
105
- data.to_hash
117
+ data.dup
106
118
  end
107
119
 
108
120
  ###
@@ -135,11 +147,11 @@ class Settings
135
147
  flattened_name_hash = {}
136
148
 
137
149
  hash.each_pair do |key, value|
138
- flattened_name_components = parent_keys.dup.push(key)
150
+ flattened_name_components = parent_keys.deep_dup.push(key)
139
151
 
140
152
  if value.respond_to?(:each_pair)
141
- flattened_name_hash.merge! to_flattened_name_hash(value,
142
- flattened_name_components)
153
+ flattened_name_hash
154
+ .deep_merge!(to_flattened_name_hash(value, flattened_name_components))
143
155
  else
144
156
  flattened_name_hash[flattened_name_components] = value
145
157
  end
@@ -182,20 +194,21 @@ class Settings
182
194
  # Returns a new Settings object
183
195
  #
184
196
  def merge(other)
185
- other_settings = if other.is_a? Settings
197
+ other_settings = case other
198
+ when Settings
186
199
  other
187
- elsif other.is_a? Hash
200
+ when ::Hash
188
201
  Settings.new(settings: other)
189
202
  end
190
203
 
191
- # rubocop:disable Metrics/LineLength
204
+ # rubocop:disable Layout/LineLength
192
205
  Settings.new(
193
206
  encryption_keys: encryption_keys.any? ? encryption_keys : other_settings.encryption_keys,
194
207
  decryption_keys: decryption_keys.any? ? decryption_keys : other_settings.decryption_keys,
195
208
  namespaces: (namespaces + other_settings.namespaces),
196
- settings: raw_data.merge(other_settings.raw_data),
209
+ settings: raw_data.deep_merge(other_settings.raw_data),
197
210
  )
198
- # rubocop:enable Metrics/LineLength
211
+ # rubocop:enable Layout/LineLength
199
212
  end
200
213
 
201
214
  ###
@@ -219,15 +232,36 @@ class Settings
219
232
  namespaces == other.namespaces
220
233
  end
221
234
 
235
+ def [](key)
236
+ warn "WARNING: Bracket access will require strings instead of symbols in Chamber 3.0. You attempted to access the '#{key}' setting. See https://github.com/thekompanee/chamber/wiki/Upgrading-To-Chamber-3.0#removal-of-bracket-indifferent-access for full details." if key.is_a?(::Symbol) # rubocop:disable Layout/LineLength
237
+ warn "WARNING: Accessing a non-existent key ('#{key}') with brackets will fail in Chamber 3.0. See https://github.com/thekompanee/chamber/wiki/Upgrading-To-Chamber-3.0#bracket-access-now-fails-on-non-existent-keys for full details." unless data.has_key?(key) # rubocop:disable Layout/LineLength
238
+
239
+ data.fetch(key)
240
+ end
241
+
242
+ def dig!(*args)
243
+ args.inject(data) do |data_value, bracket_value|
244
+ key = bracket_value.is_a?(::Symbol) ? bracket_value.to_s : bracket_value
245
+
246
+ data_value.fetch(key)
247
+ end
248
+ end
249
+
250
+ def dig(*args)
251
+ dig!(*args)
252
+ rescue ::KeyError, ::IndexError # rubocop:disable Lint/ShadowedException
253
+ nil
254
+ end
255
+
222
256
  def securable
223
- Settings.new(metadata.merge(
257
+ Settings.new(**metadata.deep_merge(
224
258
  settings: raw_data,
225
259
  pre_filters: [Filters::SecureFilter],
226
260
  ))
227
261
  end
228
262
 
229
263
  def secure
230
- Settings.new(metadata.merge(
264
+ Settings.new(**metadata.deep_merge(
231
265
  settings: raw_data,
232
266
  pre_filters: [Filters::EncryptionFilter],
233
267
  post_filters: [Filters::TranslateSecureKeysFilter],
@@ -235,29 +269,15 @@ class Settings
235
269
  end
236
270
 
237
271
  def insecure
238
- Settings.new(metadata.merge(
272
+ Settings.new(**metadata.deep_merge(
239
273
  settings: raw_data,
240
274
  pre_filters: [Filters::InsecureFilter],
241
275
  post_filters: [Filters::TranslateSecureKeysFilter],
242
276
  ))
243
277
  end
244
278
 
245
- def method_missing(name, *args)
246
- return data.public_send(name, *args) if data.respond_to?(name)
247
-
248
- super
249
- end
250
-
251
- def respond_to_missing?(name, include_private = false)
252
- data.respond_to?(name, include_private)
253
- end
254
-
255
279
  protected
256
280
 
257
- def raw_data=(new_raw_data)
258
- @raw_data = Hashie::Mash.new(new_raw_data)
259
- end
260
-
261
281
  def namespaces=(raw_namespaces)
262
282
  @namespaces = NamespaceSet.new(raw_namespaces)
263
283
  end
@@ -265,21 +285,16 @@ class Settings
265
285
  # rubocop:disable Naming/MemoizedInstanceVariableName
266
286
  def raw_data
267
287
  @filtered_raw_data ||= pre_filters.inject(@raw_data) do |filtered_data, filter|
268
- filter.execute({ data: filtered_data }.
269
- merge(metadata))
288
+ filter.execute(**{ data: filtered_data }.deep_merge(metadata))
270
289
  end
271
290
  end
272
291
  # rubocop:enable Naming/MemoizedInstanceVariableName
273
292
 
274
293
  def data
275
- @data ||= post_filters.inject(raw_data) do |filtered_data, filter|
276
- filter.execute({ data: filtered_data }.
277
- merge(metadata))
278
- end
279
- end
280
-
281
- def secure_key_prefix
282
- '_secure_'
294
+ @data ||= post_filters
295
+ .inject(raw_data) do |filtered_data, filter|
296
+ filter.execute(**{ data: filtered_data }.deep_merge(metadata))
297
+ end
283
298
  end
284
299
 
285
300
  def metadata