chamber 2.12.2 → 2.14.0

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 (58) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -2
  4. data/README.md +101 -26
  5. data/lib/chamber.rb +72 -10
  6. data/lib/chamber/adapters/cloud/circle_ci.rb +85 -0
  7. data/lib/chamber/adapters/cloud/heroku.rb +74 -0
  8. data/lib/chamber/binary/circle_ci.rb +122 -0
  9. data/lib/chamber/binary/heroku.rb +45 -16
  10. data/lib/chamber/binary/runner.rb +42 -26
  11. data/lib/chamber/binary/travis.rb +5 -3
  12. data/lib/chamber/commands/base.rb +10 -16
  13. data/lib/chamber/commands/cloud/base.rb +35 -0
  14. data/lib/chamber/commands/{heroku → cloud}/clear.rb +6 -8
  15. data/lib/chamber/commands/cloud/compare.rb +26 -0
  16. data/lib/chamber/commands/cloud/pull.rb +29 -0
  17. data/lib/chamber/commands/cloud/push.rb +44 -0
  18. data/lib/chamber/commands/comparable.rb +2 -2
  19. data/lib/chamber/commands/compare.rb +6 -9
  20. data/lib/chamber/commands/initialize.rb +26 -22
  21. data/lib/chamber/commands/securable.rb +10 -10
  22. data/lib/chamber/commands/secure.rb +2 -2
  23. data/lib/chamber/commands/show.rb +8 -8
  24. data/lib/chamber/commands/sign.rb +2 -2
  25. data/lib/chamber/commands/verify.rb +2 -2
  26. data/lib/chamber/configuration.rb +8 -3
  27. data/lib/chamber/context_resolver.rb +16 -7
  28. data/lib/chamber/encryption_methods/ssl.rb +21 -12
  29. data/lib/chamber/errors/environment_conversion.rb +8 -0
  30. data/lib/chamber/file.rb +22 -20
  31. data/lib/chamber/file_set.rb +21 -11
  32. data/lib/chamber/files/signature.rb +31 -23
  33. data/lib/chamber/filters/decryption_filter.rb +13 -11
  34. data/lib/chamber/filters/encryption_filter.rb +17 -8
  35. data/lib/chamber/filters/environment_filter.rb +21 -10
  36. data/lib/chamber/filters/failed_decryption_filter.rb +6 -6
  37. data/lib/chamber/filters/insecure_filter.rb +12 -3
  38. data/lib/chamber/filters/namespace_filter.rb +5 -5
  39. data/lib/chamber/filters/secure_filter.rb +5 -5
  40. data/lib/chamber/filters/translate_secure_keys_filter.rb +5 -5
  41. data/lib/chamber/instance.rb +48 -32
  42. data/lib/chamber/integrations/rails.rb +1 -1
  43. data/lib/chamber/integrations/sinatra.rb +6 -6
  44. data/lib/chamber/key_pair.rb +8 -8
  45. data/lib/chamber/keys/base.rb +35 -41
  46. data/lib/chamber/keys/decryption.rb +8 -14
  47. data/lib/chamber/keys/encryption.rb +8 -14
  48. data/lib/chamber/namespace_set.rb +2 -4
  49. data/lib/chamber/settings.rb +58 -54
  50. data/lib/chamber/types/secured.rb +8 -10
  51. data/lib/chamber/version.rb +1 -1
  52. data/templates/settings.yml +2 -0
  53. metadata +52 -41
  54. metadata.gz.sig +0 -0
  55. data/lib/chamber/commands/heroku.rb +0 -31
  56. data/lib/chamber/commands/heroku/compare.rb +0 -33
  57. data/lib/chamber/commands/heroku/pull.rb +0 -30
  58. data/lib/chamber/commands/heroku/push.rb +0 -27
@@ -9,7 +9,7 @@ class Rails < ::Rails::Railtie
9
9
  Chamber.load(basepath: ::Rails.root.join('config'),
10
10
  namespaces: {
11
11
  environment: -> { ::Rails.env },
12
- hostname: -> { Socket.gethostname },
12
+ hostname: -> { ::Socket.gethostname },
13
13
  })
14
14
  end
15
15
  end
@@ -16,12 +16,12 @@ module Sinatra
16
16
  end
17
17
 
18
18
  Chamber.load(
19
- basepath: root,
20
- namespaces: {
21
- environment: -> { env },
22
- hostname: -> { Socket.gethostname },
23
- },
24
- )
19
+ basepath: root,
20
+ namespaces: {
21
+ environment: -> { env },
22
+ hostname: -> { Socket.gethostname },
23
+ },
24
+ )
25
25
  end
26
26
  end
27
27
  end
@@ -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
@@ -77,10 +77,10 @@ class KeyPair
77
77
  def base_key_filename
78
78
  @base_key_filename ||= [
79
79
  '.chamber',
80
- namespace,
81
- ].
82
- compact.
83
- join('.')
80
+ namespace ? namespace.tr('-.', '') : nil,
81
+ ]
82
+ .compact
83
+ .join('.')
84
84
  end
85
85
  end
86
86
  end
@@ -3,73 +3,67 @@
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
21
- filenames.each_with_object({}) do |filename, memo|
22
- namespace = namespace_from_filename(filename) || '__default'
23
- value = key_from_file_contents(filename) ||
24
- key_from_environment_variable(filename)
21
+ key_paths.each_with_object({}) do |path, memo|
22
+ namespace = namespace_from_path(path) || '__default'
23
+ value = path.readable? ? path.read : ENV[environment_variable_from_path(path)]
25
24
 
26
25
  memo[namespace.downcase.to_sym] = value if value
27
26
  end
28
27
  end
29
28
 
30
- def filenames=(other)
31
- @filenames = begin
32
- paths = Array(other).
33
- map { |o| Pathname.new(o) }.
34
- compact
29
+ def as_environment_variables
30
+ key_paths.select(&:readable?).each_with_object({}) do |path, memo|
31
+ memo[environment_variable_from_path(path)] = path.read
32
+ end
33
+ end
35
34
 
36
- paths << default_key_file_path if paths.empty?
35
+ private
37
36
 
38
- (
39
- paths +
40
- generate_key_filenames
41
- ).
42
- uniq
43
- end
37
+ def key_paths
38
+ @key_paths = (filenames.any? ? filenames : [default_key_file_path]) +
39
+ namespaces.map { |n| namespace_to_key_path(n) }
44
40
  end
45
41
 
46
- private
42
+ # rubocop:disable Performance/ChainArrayAllocation
43
+ def filenames=(other)
44
+ @filenames = Array(other)
45
+ .map { |o| Pathname.new(o) }
46
+ .compact
47
+ end
48
+ # rubocop:enable Performance/ChainArrayAllocation
47
49
 
48
50
  def namespaces=(other)
49
- @namespaces ||= begin
50
- keys = if other.respond_to?(:keys)
51
- other.keys.map(&:to_s)
52
- else
53
- other
54
- end
55
-
56
- keys + %w{signature}
57
- end
51
+ @namespaces = other + %w{signature}
58
52
  end
59
53
 
60
- def key_from_file_contents(filename)
61
- filename.readable? && filename.read
54
+ def namespace_from_path(path)
55
+ path
56
+ .basename
57
+ .to_s
58
+ .match(self.class::NAMESPACE_PATTERN) { |m| m[1].upcase }
62
59
  end
63
60
 
64
- def key_from_environment_variable(filename)
65
- ENV[environment_variable_from_filename(filename)]
61
+ def namespace_to_key_path(namespace)
62
+ rootpath + ".chamber.#{namespace.to_s.tr('.-', '')}#{key_filename_extension}"
66
63
  end
67
64
 
68
- def namespace_from_filename(filename)
69
- filename.
70
- basename.
71
- to_s.
72
- match(self.class::NAMESPACE_PATTERN) { |m| m[1].upcase }
65
+ def default_key_file_path
66
+ Pathname.new(rootpath + ".chamber#{key_filename_extension}")
73
67
  end
74
68
  end
75
69
  end
@@ -13,28 +13,22 @@ class Decryption < Chamber::Keys::Base
13
13
  (\w+) # Namespace
14
14
  \.pem # Extension
15
15
  \z # End of Filename
16
- /x
16
+ /x.freeze
17
17
 
18
18
  private
19
19
 
20
- def environment_variable_from_filename(filename)
20
+ def environment_variable_from_path(path)
21
21
  [
22
22
  'CHAMBER',
23
- namespace_from_filename(filename),
23
+ namespace_from_path(path),
24
24
  'KEY',
25
- ].
26
- compact.
27
- join('_')
25
+ ]
26
+ .compact
27
+ .join('_')
28
28
  end
29
29
 
30
- def generate_key_filenames
31
- namespaces.map do |namespace|
32
- rootpath + ".chamber.#{namespace}.pem"
33
- end
34
- end
35
-
36
- def default_key_file_path
37
- Pathname.new(rootpath + '.chamber.pem')
30
+ def key_filename_extension
31
+ '.pem'
38
32
  end
39
33
  end
40
34
  end
@@ -13,28 +13,22 @@ class Encryption < Chamber::Keys::Base
13
13
  (\w+) # Namespace
14
14
  \.pub\.pem # Extension
15
15
  \z # End of Filename
16
- /x
16
+ /x.freeze
17
17
 
18
18
  private
19
19
 
20
- def environment_variable_from_filename(filename)
20
+ def environment_variable_from_path(path)
21
21
  [
22
22
  'CHAMBER',
23
- namespace_from_filename(filename),
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
- def generate_key_filenames
31
- namespaces.map do |namespace|
32
- rootpath + ".chamber.#{namespace}.pub.pem"
33
- end
34
- end
35
-
36
- def default_key_file_path
37
- Pathname.new(rootpath + '.chamber.pub.pem')
30
+ def key_filename_extension
31
+ '.pub.pem'
38
32
  end
39
33
  end
40
34
  end
@@ -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
  ###
@@ -16,29 +16,40 @@ require 'chamber/filters/failed_decryption_filter'
16
16
  #
17
17
  module Chamber
18
18
  class Settings
19
- attr_accessor :pre_filters,
20
- :post_filters,
19
+ attr_accessor :decryption_keys,
21
20
  :encryption_keys,
22
- :decryption_keys
21
+ :post_filters,
22
+ :pre_filters,
23
+ :secure_key_prefix
23
24
  attr_reader :namespaces
24
25
 
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
- ]
26
+ # rubocop:disable Metrics/ParameterLists
27
+ def initialize(
28
+ decryption_keys: {},
29
+ encryption_keys: {},
30
+ namespaces: [],
31
+ pre_filters: [
32
+ Filters::NamespaceFilter,
33
+ ],
34
+ post_filters: [
35
+ Filters::DecryptionFilter,
36
+ Filters::EnvironmentFilter,
37
+ Filters::FailedDecryptionFilter,
38
+ Filters::TranslateSecureKeysFilter,
39
+ ],
40
+ secure_key_prefix: '_secure_',
41
+ settings: {},
42
+ **_args
43
+ )
44
+ self.decryption_keys = decryption_keys
45
+ self.encryption_keys = encryption_keys
46
+ self.namespaces = namespaces
47
+ self.post_filters = post_filters
48
+ self.pre_filters = pre_filters
49
+ self.raw_data = settings
50
+ self.secure_key_prefix = secure_key_prefix
40
51
  end
41
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/LineLength
52
+ # rubocop:enable Metrics/ParameterLists
42
53
 
43
54
  ###
44
55
  # Internal: Converts a Settings object into a hash that is compatible as an
@@ -79,15 +90,11 @@ class Settings
79
90
  # } ).to_s
80
91
  # # => 'MY_KEY="my value" MY_OTHER_KEY="my other value"'
81
92
  #
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|
93
+ def to_s(hierarchical_separator: '_',
94
+ pair_separator: ' ',
95
+ value_surrounder: '"',
96
+ name_value_separator: '=')
97
+ pairs = to_concatenated_name_hash(hierarchical_separator).to_a.map do |key, value|
91
98
  "#{key.upcase}#{name_value_separator}#{value_surrounder}#{value}#{value_surrounder}"
92
99
  end
93
100
 
@@ -182,20 +189,21 @@ class Settings
182
189
  # Returns a new Settings object
183
190
  #
184
191
  def merge(other)
185
- other_settings = if other.is_a? Settings
192
+ other_settings = case other
193
+ when Settings
186
194
  other
187
- elsif other.is_a? Hash
195
+ when Hash
188
196
  Settings.new(settings: other)
189
197
  end
190
198
 
191
- # rubocop:disable Metrics/LineLength
199
+ # rubocop:disable Layout/LineLength
192
200
  Settings.new(
193
201
  encryption_keys: encryption_keys.any? ? encryption_keys : other_settings.encryption_keys,
194
202
  decryption_keys: decryption_keys.any? ? decryption_keys : other_settings.decryption_keys,
195
203
  namespaces: (namespaces + other_settings.namespaces),
196
204
  settings: raw_data.merge(other_settings.raw_data),
197
205
  )
198
- # rubocop:enable Metrics/LineLength
206
+ # rubocop:enable Layout/LineLength
199
207
  end
200
208
 
201
209
  ###
@@ -220,26 +228,26 @@ class Settings
220
228
  end
221
229
 
222
230
  def securable
223
- Settings.new(metadata.merge(
224
- settings: raw_data,
225
- pre_filters: [Filters::SecureFilter],
226
- ))
231
+ Settings.new(**metadata.merge(
232
+ settings: raw_data,
233
+ pre_filters: [Filters::SecureFilter],
234
+ ))
227
235
  end
228
236
 
229
237
  def secure
230
- Settings.new(metadata.merge(
231
- settings: raw_data,
232
- pre_filters: [Filters::EncryptionFilter],
233
- post_filters: [Filters::TranslateSecureKeysFilter],
234
- ))
238
+ Settings.new(**metadata.merge(
239
+ settings: raw_data,
240
+ pre_filters: [Filters::EncryptionFilter],
241
+ post_filters: [Filters::TranslateSecureKeysFilter],
242
+ ))
235
243
  end
236
244
 
237
245
  def insecure
238
- Settings.new(metadata.merge(
239
- settings: raw_data,
240
- pre_filters: [Filters::InsecureFilter],
241
- post_filters: [Filters::TranslateSecureKeysFilter],
242
- ))
246
+ Settings.new(**metadata.merge(
247
+ settings: raw_data,
248
+ pre_filters: [Filters::InsecureFilter],
249
+ post_filters: [Filters::TranslateSecureKeysFilter],
250
+ ))
243
251
  end
244
252
 
245
253
  def method_missing(name, *args)
@@ -262,24 +270,20 @@ class Settings
262
270
  @namespaces = NamespaceSet.new(raw_namespaces)
263
271
  end
264
272
 
273
+ # rubocop:disable Naming/MemoizedInstanceVariableName
265
274
  def raw_data
266
275
  @filtered_raw_data ||= pre_filters.inject(@raw_data) do |filtered_data, filter|
267
- filter.execute({ data: filtered_data }.
268
- merge(metadata))
276
+ filter.execute(**{ data: filtered_data }.merge(metadata))
269
277
  end
270
278
  end
279
+ # rubocop:enable Naming/MemoizedInstanceVariableName
271
280
 
272
281
  def data
273
282
  @data ||= post_filters.inject(raw_data) do |filtered_data, filter|
274
- filter.execute({ data: filtered_data }.
275
- merge(metadata))
283
+ filter.execute(**{ data: filtered_data }.merge(metadata))
276
284
  end
277
285
  end
278
286
 
279
- def secure_key_prefix
280
- '_secure_'
281
- end
282
-
283
287
  def metadata
284
288
  {
285
289
  decryption_keys: decryption_keys,
@@ -3,21 +3,19 @@
3
3
  require 'active_support/json'
4
4
  require 'chamber'
5
5
 
6
- # rubocop:disable Lint/HandleExceptions, Layout/EmptyLinesAroundModuleBody
7
6
  module Chamber
8
-
9
7
  begin
10
8
  require 'active_record/type/value'
11
9
 
12
10
  CHAMBER_TYPE_VALUE_SUPERCLASS = ActiveRecord::Type::Value
13
- rescue LoadError
11
+ rescue LoadError # rubocop:disable Lint/SuppressedException
14
12
  end
15
13
 
16
14
  begin
17
15
  require 'active_model/type/value'
18
16
 
19
17
  CHAMBER_TYPE_VALUE_SUPERCLASS = ActiveModel::Type::Value
20
- rescue LoadError
18
+ rescue LoadError # rubocop:disable Lint/SuppressedException
21
19
  end
22
20
 
23
21
  module Types
@@ -25,11 +23,12 @@ class Secured < CHAMBER_TYPE_VALUE_SUPERCLASS
25
23
  attr_accessor :decryption_keys,
26
24
  :encryption_keys
27
25
 
28
- def initialize(options = {})
29
- self.encryption_keys = options.fetch(:encryption_keys,
30
- Chamber.configuration.encryption_keys)
31
- self.decryption_keys = options.fetch(:decryption_keys,
32
- Chamber.configuration.decryption_keys)
26
+ def initialize(decryption_keys: ::Chamber.configuration.decryption_keys,
27
+ encryption_keys: ::Chamber.configuration.encryption_keys)
28
+ self.decryption_keys = decryption_keys
29
+ self.encryption_keys = encryption_keys
30
+
31
+ super()
33
32
  end
34
33
 
35
34
  def type
@@ -78,4 +77,3 @@ class Secured < CHAMBER_TYPE_VALUE_SUPERCLASS
78
77
  end
79
78
  end
80
79
  end
81
- # rubocop:enable Lint/HandleExceptions, Layout/EmptyLinesAroundModuleBody