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
@@ -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