chamber 2.12.5 → 2.14.2

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 (55) 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 +82 -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 +9 -9
  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 +8 -7
  28. data/lib/chamber/encryption_methods/ssl.rb +12 -12
  29. data/lib/chamber/file.rb +16 -14
  30. data/lib/chamber/file_set.rb +18 -8
  31. data/lib/chamber/files/signature.rb +16 -14
  32. data/lib/chamber/filters/decryption_filter.rb +17 -13
  33. data/lib/chamber/filters/encryption_filter.rb +8 -8
  34. data/lib/chamber/filters/environment_filter.rb +12 -14
  35. data/lib/chamber/filters/failed_decryption_filter.rb +6 -6
  36. data/lib/chamber/filters/insecure_filter.rb +3 -3
  37. data/lib/chamber/filters/namespace_filter.rb +5 -5
  38. data/lib/chamber/filters/secure_filter.rb +5 -5
  39. data/lib/chamber/filters/translate_secure_keys_filter.rb +5 -5
  40. data/lib/chamber/instance.rb +45 -21
  41. data/lib/chamber/key_pair.rb +7 -7
  42. data/lib/chamber/keys/base.rb +31 -49
  43. data/lib/chamber/keys/decryption.rb +5 -5
  44. data/lib/chamber/keys/encryption.rb +5 -5
  45. data/lib/chamber/namespace_set.rb +2 -4
  46. data/lib/chamber/settings.rb +73 -45
  47. data/lib/chamber/types/secured.rb +8 -10
  48. data/lib/chamber/version.rb +1 -1
  49. data/templates/settings.yml +2 -0
  50. metadata +46 -39
  51. metadata.gz.sig +0 -0
  52. data/lib/chamber/commands/heroku.rb +0 -31
  53. data/lib/chamber/commands/heroku/compare.rb +0 -33
  54. data/lib/chamber/commands/heroku/pull.rb +0 -30
  55. data/lib/chamber/commands/heroku/push.rb +0 -27
@@ -114,17 +114,26 @@ module Chamber
114
114
  class FileSet
115
115
  attr_accessor :decryption_keys,
116
116
  :encryption_keys,
117
- :basepath
117
+ :basepath,
118
+ :signature_name
118
119
  attr_reader :namespaces,
119
120
  :paths
120
121
 
121
- def initialize(options = {})
122
- self.namespaces = options[:namespaces] || {}
123
- self.decryption_keys = options[:decryption_keys]
124
- self.encryption_keys = options[:encryption_keys]
125
- self.paths = options.fetch(:files)
126
- self.basepath = options[:basepath]
122
+ # rubocop:disable Metrics/ParameterLists
123
+ def initialize(files:,
124
+ basepath: nil,
125
+ decryption_keys: nil,
126
+ encryption_keys: nil,
127
+ namespaces: {},
128
+ signature_name: nil)
129
+ self.basepath = basepath
130
+ self.decryption_keys = decryption_keys
131
+ self.encryption_keys = encryption_keys
132
+ self.namespaces = namespaces
133
+ self.paths = files
134
+ self.signature_name = signature_name
127
135
  end
136
+ # rubocop:enable Metrics/ParameterLists
128
137
 
129
138
  ###
130
139
  # Internal: Returns an Array of the ordered list of files that was processed
@@ -234,7 +243,8 @@ class FileSet
234
243
  File.new(path: file,
235
244
  namespaces: namespaces,
236
245
  decryption_keys: decryption_keys,
237
- encryption_keys: encryption_keys)
246
+ encryption_keys: encryption_keys,
247
+ signature_name: signature_name)
238
248
  end
239
249
 
240
250
  sorted_relevant_files += relevant_glob_files
@@ -8,9 +8,9 @@ module Chamber
8
8
  module Files
9
9
  class Signature
10
10
  SIGNATURE_HEADER = '-----BEGIN CHAMBER SIGNATURE-----'
11
- SIGNATURE_HEADER_PATTERN = /\-\-\-\-\-BEGIN\sCHAMBER\sSIGNATURE\-\-\-\-\-/.freeze
11
+ SIGNATURE_HEADER_PATTERN = /-----BEGIN\sCHAMBER\sSIGNATURE-----/.freeze
12
12
  SIGNATURE_FOOTER = '-----END CHAMBER SIGNATURE-----'
13
- SIGNATURE_FOOTER_PATTERN = /\-\-\-\-\-END\sCHAMBER\sSIGNATURE\-\-\-\-\-/.freeze
13
+ SIGNATURE_FOOTER_PATTERN = /-----END\sCHAMBER\sSIGNATURE-----/.freeze
14
14
  SIGNATURE_IN_FILE_PATTERN = /
15
15
  #{SIGNATURE_HEADER_PATTERN}\n # Header
16
16
  (.*)\n # Signature Body
@@ -18,14 +18,16 @@ class Signature
18
18
  /x.freeze
19
19
 
20
20
  attr_accessor :settings_content,
21
- :settings_filename
21
+ :settings_filename,
22
+ :signature_name
22
23
 
23
24
  attr_reader :signature_key
24
25
 
25
- def initialize(settings_filename, settings_content, signature_key)
26
+ def initialize(settings_filename, settings_content, signature_key, signature_name)
26
27
  self.signature_key = signature_key
27
28
  self.settings_content = settings_content
28
29
  self.settings_filename = Pathname.new(settings_filename)
30
+ self.signature_name = signature_name
29
31
  end
30
32
 
31
33
  def signature_key=(keyish)
@@ -41,7 +43,7 @@ class Signature
41
43
 
42
44
  def write
43
45
  signature_filename.write(<<-HEREDOC, 0, mode: 'w+')
44
- Signed By: #{`git config --get 'user.name'`.chomp}
46
+ Signed By: #{signature_name}
45
47
  Signed At: #{Time.now.utc.iso8601}
46
48
 
47
49
  #{SIGNATURE_HEADER}
@@ -61,20 +63,20 @@ Signed At: #{Time.now.utc.iso8601}
61
63
  end
62
64
 
63
65
  def raw_signature
64
- @raw_signature ||= signature_key.
65
- sign(digest, settings_content)
66
+ @raw_signature ||= signature_key
67
+ .sign(digest, settings_content)
66
68
  end
67
69
 
68
70
  def signature_filename
69
- @signature_filename ||= settings_filename.
70
- sub('.yml', '.sig').
71
- sub('.erb', '')
71
+ @signature_filename ||= settings_filename
72
+ .sub('.yml', '.sig')
73
+ .sub('.erb', '')
72
74
  end
73
75
 
74
76
  def encoded_signature_content
75
- @encoded_signature_content ||= signature_filename.
76
- read.
77
- match(SIGNATURE_IN_FILE_PATTERN) do |match|
77
+ @encoded_signature_content ||= signature_filename
78
+ .read
79
+ .match(SIGNATURE_IN_FILE_PATTERN) do |match|
78
80
  match[1]
79
81
  end
80
82
  end
@@ -84,7 +86,7 @@ Signed At: #{Time.now.utc.iso8601}
84
86
  end
85
87
 
86
88
  def digest
87
- @digest ||= OpenSSL::Digest::SHA512.new
89
+ @digest ||= OpenSSL::Digest.new('SHA512')
88
90
  end
89
91
  end
90
92
  end
@@ -12,19 +12,19 @@ require 'chamber/errors/decryption_failure'
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
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
16
16
  LARGE_DATA_STRING_PATTERN = %r{
17
17
  \A # Beginning of String
18
18
  (
19
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Key
19
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Key
20
20
  )
21
21
  \# # Separator
22
22
  (
23
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded IV
23
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded IV
24
24
  )
25
25
  \# # Separator
26
26
  (
27
- [A-Za-z0-9\+\/#]*\={0,2} # Base64 Encoded Data
27
+ [A-Za-z0-9+/#]*={0,2} # Base64 Encoded Data
28
28
  )
29
29
  \z # End of String
30
30
  }x.freeze
@@ -33,14 +33,14 @@ class DecryptionFilter
33
33
  :secure_key_token
34
34
  attr_reader :decryption_keys
35
35
 
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))}/
36
+ def initialize(data:, secure_key_prefix:, decryption_keys: {}, **_args)
37
+ self.decryption_keys = decryption_keys || {}
38
+ self.data = data.dup
39
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
40
40
  end
41
41
 
42
- def self.execute(options = {})
43
- new(options).__send__(:execute)
42
+ def self.execute(**args)
43
+ new(**args).__send__(:execute)
44
44
  end
45
45
 
46
46
  protected
@@ -75,17 +75,21 @@ class DecryptionFilter
75
75
 
76
76
  private
77
77
 
78
+ # rubocop:disable Style/RedundantBegin
78
79
  def decrypt(key, value)
79
80
  method = decryption_method(value)
80
81
 
81
82
  decryption_keys.each do |decryption_key|
82
- return method.decrypt(key, value, decryption_key)
83
- rescue OpenSSL::PKey::RSAError
84
- next
83
+ begin
84
+ return method.decrypt(key, value, decryption_key)
85
+ rescue OpenSSL::PKey::RSAError
86
+ next
87
+ end
85
88
  end
86
89
 
87
90
  value
88
91
  end
92
+ # rubocop:enable Style/RedundantBegin
89
93
 
90
94
  def decryption_method(value)
91
95
  if value.respond_to?(:match)
@@ -10,8 +10,8 @@ require 'chamber/encryption_methods/none'
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
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
14
+ BASE64_SUBSTRING_PATTERN = %r{[A-Za-z0-9+/#]*={0,2}}.freeze
15
15
  LARGE_DATA_STRING_PATTERN = /
16
16
  \A
17
17
  (#{BASE64_SUBSTRING_PATTERN})
@@ -26,14 +26,14 @@ class EncryptionFilter
26
26
  :secure_key_token
27
27
  attr_reader :encryption_keys
28
28
 
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))}/
29
+ def initialize(data:, secure_key_prefix:, encryption_keys: {}, **_args)
30
+ self.encryption_keys = encryption_keys || {}
31
+ self.data = data.dup
32
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
33
33
  end
34
34
 
35
- def self.execute(options = {})
36
- new(options).__send__(:execute)
35
+ def self.execute(**args)
36
+ new(**args).__send__(:execute)
37
37
  end
38
38
 
39
39
  protected
@@ -88,16 +88,16 @@ class EnvironmentFilter
88
88
  # }
89
89
  #
90
90
  #
91
- def self.execute(options = {})
92
- new(options).__send__(:execute)
91
+ def self.execute(**args)
92
+ new(**args).__send__(:execute)
93
93
  end
94
94
 
95
95
  attr_accessor :data,
96
96
  :secure_key_token
97
97
 
98
- def initialize(options = {})
99
- self.data = options.fetch(:data)
100
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
98
+ def initialize(data:, secure_key_prefix:, **_args)
99
+ self.data = data
100
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
101
101
  end
102
102
 
103
103
  protected
@@ -138,8 +138,7 @@ class EnvironmentFilter
138
138
  environment_hash
139
139
  end
140
140
 
141
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
142
- def convert_environment_value(environment_key, environment_value, settings_value)
141
+ def convert_environment_value(environment_key, environment_value, settings_value) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
143
142
  return settings_value unless environment_value
144
143
  return if %w{___nil___ ___null___}.include?(environment_value)
145
144
 
@@ -163,22 +162,21 @@ class EnvironmentFilter
163
162
  fail ArgumentError, "Invalid value for Array: #{environment_value}"
164
163
  end
165
164
  end
166
- when 'Integer'
165
+ when 'Fixnum', 'Integer'
167
166
  Integer(environment_value)
168
167
  else
169
168
  environment_value
170
169
  end
171
170
  rescue ArgumentError
172
- raise Chamber::Errors::EnvironmentConversion, <<~HEREDOC
173
- We attempted to convert '#{environment_key}' from '#{environment_value}' to a '#{settings_value.class.name}'.
171
+ raise Chamber::Errors::EnvironmentConversion, <<-HEREDOC
172
+ We attempted to convert '#{environment_key}' from '#{environment_value}' to a '#{settings_value.class.name}'.
174
173
 
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).
174
+ Unfortunately, this did not go as planned. Please either verify that your value is convertable
175
+ or change the original YAML value to be something more generic (like a String).
177
176
 
178
- For more information, see https://github.com/thekompanee/chamber/wiki/Environment-Variable-Coercions
177
+ For more information, see https://github.com/thekompanee/chamber/wiki/Environment-Variable-Coercions
179
178
  HEREDOC
180
179
  end
181
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
182
180
  end
183
181
  end
184
182
  end
@@ -5,18 +5,18 @@ require 'chamber/errors/decryption_failure'
5
5
  module Chamber
6
6
  module Filters
7
7
  class FailedDecryptionFilter
8
- BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+/]{342}==\z}.freeze
8
+ BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9+/]{342}==\z}.freeze
9
9
 
10
- def self.execute(options = {})
11
- new(options).__send__(:execute)
10
+ def self.execute(**args)
11
+ new(**args).__send__(:execute)
12
12
  end
13
13
 
14
14
  attr_accessor :data,
15
15
  :secure_key_token
16
16
 
17
- def initialize(options = {})
18
- self.data = options.fetch(:data).dup
19
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
17
+ def initialize(data:, secure_key_prefix:, **_args)
18
+ self.data = data.dup
19
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
20
20
  end
21
21
 
22
22
  protected
@@ -6,8 +6,8 @@ require 'chamber/filters/secure_filter'
6
6
  module Chamber
7
7
  module Filters
8
8
  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
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
11
11
  LARGE_DATA_STRING_PATTERN = /
12
12
  \A
13
13
  (#{BASE64_SUBSTRING_PATTERN})
@@ -20,7 +20,7 @@ class InsecureFilter < SecureFilter
20
20
 
21
21
  protected
22
22
 
23
- def execute(raw_data = data)
23
+ def execute(raw_data = data) # rubocop:disable Metrics/CyclomaticComplexity
24
24
  securable_settings = super
25
25
  settings = Hashie::Mash.new
26
26
 
@@ -5,16 +5,16 @@ require 'hashie/mash'
5
5
  module Chamber
6
6
  module Filters
7
7
  class NamespaceFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
8
+ def self.execute(**args)
9
+ new(**args).__send__(:execute)
10
10
  end
11
11
 
12
12
  attr_accessor :data,
13
13
  :namespaces
14
14
 
15
- def initialize(options = {})
16
- self.data = Hashie::Mash.new(options.fetch(:data))
17
- self.namespaces = options.fetch(:namespaces)
15
+ def initialize(data:, namespaces:, **_args)
16
+ self.data = Hashie::Mash.new(data)
17
+ self.namespaces = namespaces
18
18
  end
19
19
 
20
20
  protected
@@ -5,16 +5,16 @@ require 'hashie/mash'
5
5
  module Chamber
6
6
  module Filters
7
7
  class SecureFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
8
+ def self.execute(**args)
9
+ new(**args).__send__(:execute)
10
10
  end
11
11
 
12
12
  attr_accessor :data,
13
13
  :secure_key_token
14
14
 
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))}/
15
+ def initialize(data:, secure_key_prefix:, **_args)
16
+ self.data = Hashie::Mash.new(data)
17
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
18
18
  end
19
19
 
20
20
  protected
@@ -5,16 +5,16 @@ require 'hashie/mash'
5
5
  module Chamber
6
6
  module Filters
7
7
  class TranslateSecureKeysFilter
8
- def self.execute(options = {})
9
- new(options).__send__(:execute)
8
+ def self.execute(**args)
9
+ new(**args).__send__(:execute)
10
10
  end
11
11
 
12
12
  attr_accessor :data,
13
13
  :secure_key_token
14
14
 
15
- def initialize(options = {})
16
- self.data = options.fetch(:data).dup
17
- self.secure_key_token = /\A#{Regexp.escape(options.fetch(:secure_key_prefix))}/
15
+ def initialize(data:, secure_key_prefix:, **_args)
16
+ self.data = data.dup
17
+ self.secure_key_token = /\A#{Regexp.escape(secure_key_prefix)}/
18
18
  end
19
19
 
20
20
  protected
@@ -9,15 +9,27 @@ class Instance
9
9
  attr_accessor :configuration,
10
10
  :files
11
11
 
12
- def initialize(options = {})
13
- self.configuration = Configuration.new options
14
- self.files = FileSet.new configuration.to_hash
12
+ def initialize(**args)
13
+ self.configuration = Configuration.new(**args)
14
+ self.files = FileSet.new(**configuration.to_hash)
15
15
  end
16
16
 
17
17
  def settings
18
18
  @settings ||= files.to_settings { |settings| @settings = settings }
19
19
  end
20
20
 
21
+ def [](key)
22
+ settings.[](key)
23
+ end
24
+
25
+ def dig!(*args)
26
+ settings.dig!(*args)
27
+ end
28
+
29
+ def dig(*args)
30
+ settings.dig(*args)
31
+ end
32
+
21
33
  def filenames
22
34
  files.filenames
23
35
  end
@@ -34,26 +46,42 @@ class Instance
34
46
  files.verify
35
47
  end
36
48
 
37
- def encrypt(data, options = {})
38
- config = configuration.to_hash.merge(options)
49
+ def to_environment
50
+ settings.to_environment
51
+ end
52
+
53
+ def to_s(**args)
54
+ settings.to_s(**args)
55
+ end
56
+
57
+ def to_hash
58
+ settings.to_hash
59
+ end
60
+
61
+ def namespaces
62
+ settings.namespaces
63
+ end
64
+
65
+ def encrypt(data, **args)
66
+ config = configuration.to_hash.merge(**args)
39
67
 
40
- Settings.
41
- new(
42
- config.merge(
68
+ Settings
69
+ .new(
70
+ **config.merge(
43
71
  settings: data,
44
72
  pre_filters: [Filters::EncryptionFilter],
45
73
  post_filters: [],
46
74
  ),
47
- ).
48
- to_hash
75
+ )
76
+ .to_hash
49
77
  end
50
78
 
51
- def decrypt(data, options = {})
52
- config = configuration.to_hash.merge(options)
79
+ def decrypt(data, **args)
80
+ config = configuration.to_hash.merge(**args)
53
81
 
54
- Settings.
55
- new(
56
- config.merge(
82
+ Settings
83
+ .new(
84
+ **config.merge(
57
85
  settings: data,
58
86
  pre_filters: [Filters::NamespaceFilter],
59
87
  post_filters: [
@@ -61,12 +89,8 @@ class Instance
61
89
  Filters::FailedDecryptionFilter,
62
90
  ],
63
91
  ),
64
- ).
65
- to_hash
66
- end
67
-
68
- def to_s(options = {})
69
- settings.to_s(options)
92
+ )
93
+ .to_hash
70
94
  end
71
95
 
72
96
  def method_missing(name, *args)