chamber 2.12.2 → 2.14.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chamber/commands/cloud/base'
4
+ require 'chamber/commands/securable'
5
+ require 'chamber/keys/decryption'
6
+
7
+ module Chamber
8
+ module Commands
9
+ module Cloud
10
+ class Push < Chamber::Commands::Cloud::Base
11
+ include Chamber::Commands::Securable
12
+
13
+ attr_accessor :keys
14
+
15
+ def initialize(keys:, **args)
16
+ super(**args)
17
+
18
+ self.keys = keys
19
+ end
20
+
21
+ def call
22
+ environment_variables = if keys
23
+ Keys::Decryption
24
+ .new(rootpath: chamber.configuration.rootpath,
25
+ namespaces: chamber.configuration.namespaces)
26
+ .as_environment_variables
27
+ else
28
+ securable_environment_variables
29
+ end
30
+
31
+ environment_variables.each do |key, value|
32
+ if dry_run
33
+ shell.say_status 'push', key, :blue
34
+ else
35
+ shell.say_status 'push', key, :green
36
+
37
+ adapter.add_environment_variable(key, value)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -5,10 +5,10 @@ require 'tempfile'
5
5
  module Chamber
6
6
  module Commands
7
7
  module Comparable
8
- def initialize(options = {})
8
+ def initialize(keys_only:, **args)
9
9
  super
10
10
 
11
- self.keys_only = options[:keys_only]
11
+ self.keys_only = keys_only
12
12
  end
13
13
 
14
14
  def call
@@ -12,18 +12,15 @@ class Compare < Chamber::Commands::Base
12
12
  attr_accessor :first_settings_instance,
13
13
  :second_settings_instance
14
14
 
15
- def self.call(options = {})
16
- new(options).call
15
+ def self.call(**args)
16
+ new(**args).call
17
17
  end
18
18
 
19
- def initialize(options = {})
20
- super
19
+ def initialize(first:, second:, **args)
20
+ super(**args)
21
21
 
22
- first_settings_options = options.merge(namespaces: options[:first])
23
- self.first_settings_instance = Chamber::Instance.new(first_settings_options)
24
-
25
- second_settings_options = options.merge(namespaces: options[:second])
26
- self.second_settings_instance = Chamber::Instance.new(second_settings_options)
22
+ self.first_settings_instance = Chamber::Instance.new(args.merge(namespaces: first))
23
+ self.second_settings_instance = Chamber::Instance.new(args.merge(namespaces: second))
27
24
  end
28
25
 
29
26
  protected
@@ -11,23 +11,23 @@ require 'chamber/commands/base'
11
11
  module Chamber
12
12
  module Commands
13
13
  class Initialize < Chamber::Commands::Base
14
- def self.call(options = {})
15
- new(options).call
14
+ def self.call(**args)
15
+ new(**args).call
16
16
  end
17
17
 
18
18
  attr_accessor :basepath,
19
19
  :namespaces,
20
20
  :signature
21
21
 
22
- def initialize(options = {})
23
- super
22
+ def initialize(signature:, namespaces: [], **args)
23
+ super(**args)
24
24
 
25
25
  self.basepath = Chamber.configuration.basepath
26
- self.namespaces = options.fetch(:namespaces, [])
27
- self.signature = options.fetch(:signature)
26
+ self.namespaces = namespaces
27
+ self.signature = signature
28
28
  end
29
29
 
30
- # rubocop:disable Metrics/LineLength, Metrics/MethodLength, Metrics/AbcSize
30
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
31
31
  def call
32
32
  key_pairs = namespaces.map do |namespace|
33
33
  Chamber::KeyPair.new(namespace: namespace,
@@ -78,21 +78,25 @@ class Initialize < Chamber::Commands::Base
78
78
  shell.say 'Your signature keys, which will be used for verification, are located at:'
79
79
  shell.say ''
80
80
  shell.say ' * Public Key: '
81
- shell.say signature_key_pair.
82
- public_key_filepath.
83
- relative_path_from(Pathname.pwd), :yellow
81
+ shell.say signature_key_pair
82
+ .public_key_filepath
83
+ .relative_path_from(Pathname.pwd),
84
+ :yellow
84
85
  shell.say ' * Private Key: '
85
- shell.say signature_key_pair.
86
- unencrypted_private_key_filepath.
87
- relative_path_from(Pathname.pwd), :yellow
86
+ shell.say signature_key_pair
87
+ .unencrypted_private_key_filepath
88
+ .relative_path_from(Pathname.pwd),
89
+ :yellow
88
90
  shell.say ' * Encrypted Private Key: '
89
- shell.say signature_key_pair.
90
- encrypted_private_key_filepath.
91
- relative_path_from(Pathname.pwd), :yellow
91
+ shell.say signature_key_pair
92
+ .encrypted_private_key_filepath
93
+ .relative_path_from(Pathname.pwd),
94
+ :yellow
92
95
  shell.say ' * Encrypted Passphrase: '
93
- shell.say signature_key_pair.
94
- encrypted_private_key_passphrase_filepath.
95
- relative_path_from(Pathname.pwd), :yellow
96
+ shell.say signature_key_pair
97
+ .encrypted_private_key_passphrase_filepath
98
+ .relative_path_from(Pathname.pwd),
99
+ :yellow
96
100
 
97
101
  shell.say ''
98
102
  shell.say 'The signature private keys should be thought of separately from the other'
@@ -153,7 +157,7 @@ class Initialize < Chamber::Commands::Base
153
157
  shell.say '--------------------------------------------------------------------------------'
154
158
  shell.say ''
155
159
  end
156
- # rubocop:enable Metrics/LineLength, Metrics/MethodLength, Metrics/AbcSize
160
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
157
161
 
158
162
  protected
159
163
 
@@ -178,7 +182,7 @@ class Initialize < Chamber::Commands::Base
178
182
  end
179
183
 
180
184
  def append_to_gitignore
181
- ::FileUtils.touch gitignore_filepath
185
+ ::FileUtils.touch(gitignore_filepath)
182
186
 
183
187
  gitignore_contents = ::File.read(gitignore_filepath)
184
188
 
@@ -206,7 +210,7 @@ class Initialize < Chamber::Commands::Base
206
210
 
207
211
  def gem_path
208
212
  @gem_path ||= Pathname.new(
209
- ::File.expand_path('../../../..', __FILE__),
213
+ ::File.expand_path('../../..', __dir__),
210
214
  )
211
215
  end
212
216
 
@@ -6,15 +6,15 @@ require 'chamber/instance'
6
6
  module Chamber
7
7
  module Commands
8
8
  module Securable
9
- def initialize(options = {})
10
- super
11
-
12
- ignored_settings_options = options.
13
- merge(files: ignored_settings_filepaths).
14
- reject { |k, _v| k == 'basepath' }
15
- self.ignored_settings_instance = Chamber::Instance.new(ignored_settings_options)
16
- self.current_settings_instance = Chamber::Instance.new(options)
17
- self.only_sensitive = options[:only_sensitive]
9
+ def initialize(only_sensitive: nil, **args)
10
+ super(**args)
11
+
12
+ ignored_settings_options = args
13
+ .merge(files: ignored_settings_filepaths)
14
+ .reject { |k, _v| k == 'basepath' }
15
+ self.ignored_settings_instance = Chamber::Instance.new(**ignored_settings_options)
16
+ self.current_settings_instance = Chamber::Instance.new(**args)
17
+ self.only_sensitive = only_sensitive
18
18
  end
19
19
 
20
20
  protected
@@ -53,7 +53,7 @@ module Securable
53
53
  end
54
54
 
55
55
  `
56
- git ls-files --other --ignored --exclude-from=.gitignore |
56
+ git ls-files --other --ignored --exclude-per-directory=.gitignore |
57
57
  sed -e "s|^|#{Shellwords.escape(rootpath.to_s)}/|" |
58
58
  grep --colour=never -E '#{shell_escaped_chamber_filenames.join('|')}'
59
59
  `.split("\n")
@@ -8,8 +8,8 @@ module Commands
8
8
  class Secure < Chamber::Commands::Base
9
9
  include Chamber::Commands::Securable
10
10
 
11
- def initialize(options = {})
12
- super(options.merge(namespaces: ['*']))
11
+ def initialize(**args)
12
+ super(**args.merge(namespaces: ['*']))
13
13
  end
14
14
 
15
15
  def call
@@ -9,21 +9,21 @@ class Show < Chamber::Commands::Base
9
9
  attr_accessor :as_env,
10
10
  :only_sensitive
11
11
 
12
- def initialize(options = {})
13
- super
12
+ def initialize(as_env: nil, only_sensitive: nil, **args)
13
+ super(**args)
14
14
 
15
- self.as_env = options[:as_env]
16
- self.only_sensitive = options[:only_sensitive]
15
+ self.as_env = as_env
16
+ self.only_sensitive = only_sensitive
17
17
  end
18
18
 
19
19
  def call
20
20
  if as_env
21
21
  settings.to_s(pair_separator: "\n")
22
22
  else
23
- PP.
24
- pp(settings.to_hash, StringIO.new, 60).
25
- string.
26
- chomp
23
+ PP
24
+ .pp(settings.to_hash, StringIO.new, 60)
25
+ .string
26
+ .chomp
27
27
  end
28
28
  end
29
29
 
@@ -5,8 +5,8 @@ require 'chamber/commands/base'
5
5
  module Chamber
6
6
  module Commands
7
7
  class Sign < Chamber::Commands::Base
8
- def initialize(options = {})
9
- super(options.merge(namespaces: ['*']))
8
+ def initialize(**args)
9
+ super(**args.merge(namespaces: ['*']))
10
10
  end
11
11
 
12
12
  def call
@@ -5,8 +5,8 @@ require 'chamber/commands/base'
5
5
  module Chamber
6
6
  module Commands
7
7
  class Verify < Chamber::Commands::Base
8
- def initialize(options = {})
9
- super(options.merge(namespaces: ['*']))
8
+ def initialize(**args)
9
+ super(**args.merge(namespaces: ['*']))
10
10
  end
11
11
 
12
12
  def call
@@ -8,16 +8,20 @@ class Configuration
8
8
  :decryption_keys,
9
9
  :encryption_keys,
10
10
  :files,
11
- :namespaces
11
+ :namespaces,
12
+ :rootpath,
13
+ :signature_name
12
14
 
13
- def initialize(options = {})
14
- options = ContextResolver.resolve(options)
15
+ def initialize(**args)
16
+ options = ContextResolver.resolve(**args)
15
17
 
16
18
  self.basepath = options.fetch(:basepath)
17
19
  self.namespaces = options.fetch(:namespaces)
18
20
  self.decryption_keys = options.fetch(:decryption_keys)
19
21
  self.encryption_keys = options.fetch(:encryption_keys)
20
22
  self.files = options.fetch(:files)
23
+ self.rootpath = options.fetch(:rootpath)
24
+ self.signature_name = options.fetch(:signature_name)
21
25
  end
22
26
 
23
27
  def to_hash
@@ -27,6 +31,7 @@ class Configuration
27
31
  encryption_keys: encryption_keys,
28
32
  files: files,
29
33
  namespaces: namespaces,
34
+ signature_name: signature_name,
30
35
  }
31
36
  end
32
37
  end
@@ -3,7 +3,6 @@
3
3
  require 'pathname'
4
4
  require 'socket'
5
5
 
6
- require 'chamber/core_ext/hash'
7
6
  require 'chamber/keys/decryption'
8
7
  require 'chamber/keys/encryption'
9
8
 
@@ -11,11 +10,11 @@ module Chamber
11
10
  class ContextResolver
12
11
  attr_accessor :options
13
12
 
14
- def initialize(options = {})
15
- self.options = options.transform_keys(&:to_sym)
13
+ def initialize(**args)
14
+ self.options = args
16
15
  end
17
16
 
18
- # rubocop:disable Metrics/AbcSize, Metrics/LineLength
17
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Layout/LineLength
19
18
  def resolve
20
19
  options[:rootpath] ||= Pathname.pwd
21
20
  options[:rootpath] = Pathname.new(options[:rootpath])
@@ -30,6 +29,7 @@ class ContextResolver
30
29
  options[:basepath] ||= options[:rootpath]
31
30
  end
32
31
 
32
+ options[:namespaces] = resolve_namespaces(options[:namespaces])
33
33
  options[:encryption_keys] = Keys::Encryption.resolve(filenames: options[:encryption_keys],
34
34
  namespaces: options[:namespaces],
35
35
  rootpath: options[:rootpath])
@@ -42,16 +42,25 @@ class ContextResolver
42
42
  options[:basepath] + 'settings',
43
43
  ]
44
44
 
45
+ options[:signature_name] = options[:signature_name]
46
+
45
47
  options
46
48
  end
47
- # rubocop:enable Metrics/AbcSize, Metrics/LineLength
49
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Layout/LineLength
48
50
 
49
- def self.resolve(options = {})
50
- new(options).resolve
51
+ def self.resolve(**args)
52
+ new(**args).resolve
51
53
  end
52
54
 
53
55
  protected
54
56
 
57
+ def resolve_namespaces(other)
58
+ (other.respond_to?(:values) ? other.values : other)
59
+ .map do |namespace|
60
+ namespace.respond_to?(:call) ? namespace.call : namespace
61
+ end
62
+ end
63
+
55
64
  def resolve_preset
56
65
  if in_a_rails_project?
57
66
  'rails'
@@ -5,14 +5,23 @@ require 'base64'
5
5
  module Chamber
6
6
  module EncryptionMethods
7
7
  class Ssl
8
- LARGE_DATA_STRING_PATTERN = %r{\A([A-Za-z0-9\+\/#]*\={0,2})#([A-Za-z0-9\+\/#]*\={0,2})#([A-Za-z0-9\+\/#]*\={0,2})\z} # rubocop:disable Metrics/LineLength
8
+ BASE64_STRING_PATTERN = %r{[A-Za-z0-9+/#]*={0,2}}.freeze
9
+ LARGE_DATA_STRING_PATTERN = /
10
+ \A
11
+ (#{BASE64_STRING_PATTERN})
12
+ \#
13
+ (#{BASE64_STRING_PATTERN})
14
+ \#
15
+ (#{BASE64_STRING_PATTERN})
16
+ \z
17
+ /x.freeze
9
18
 
10
- def self.encrypt(_key, value, encryption_keys)
11
- value = YAML.dump(value)
12
- cipher = OpenSSL::Cipher.new('AES-128-CBC')
19
+ def self.encrypt(_key, value, encryption_keys) # rubocop:disable Metrics/AbcSize
20
+ value = YAML.dump(value)
21
+ cipher = OpenSSL::Cipher.new('AES-128-CBC')
13
22
  cipher.encrypt
14
23
  symmetric_key = cipher.random_key
15
- iv = cipher.random_iv
24
+ iv = cipher.random_iv
16
25
 
17
26
  # encrypt all data with this key and iv
18
27
  encrypted_data = cipher.update(value) + cipher.final
@@ -26,24 +35,24 @@ class Ssl
26
35
  Base64.strict_encode64(encrypted_data)
27
36
  end
28
37
 
29
- def self.decrypt(key, value, decryption_keys)
38
+ def self.decrypt(key, value, decryption_keys) # rubocop:disable Metrics/AbcSize
30
39
  if decryption_keys.nil?
31
40
  value
32
41
  else
33
- key, iv, decoded_string = value.
34
- match(LARGE_DATA_STRING_PATTERN).
35
- captures.
36
- map do |part|
42
+ key, iv, decoded_string = value
43
+ .match(LARGE_DATA_STRING_PATTERN)
44
+ .captures
45
+ .map do |part|
37
46
  Base64.strict_decode64(part)
38
47
  end
39
- key = decryption_keys.private_decrypt(key)
48
+ key = decryption_keys.private_decrypt(key)
40
49
 
41
50
  cipher_dec = OpenSSL::Cipher.new('AES-128-CBC')
42
51
 
43
52
  cipher_dec.decrypt
44
53
 
45
54
  cipher_dec.key = key
46
- cipher_dec.iv = iv
55
+ cipher_dec.iv = iv
47
56
 
48
57
  begin
49
58
  unencrypted_value = cipher_dec.update(decoded_string) + cipher_dec.final
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chamber
4
+ module Errors
5
+ class EnvironmentConversion < ArgumentError
6
+ end
7
+ end
8
+ end
@@ -13,7 +13,8 @@ module Chamber
13
13
  class File < Pathname
14
14
  attr_accessor :namespaces,
15
15
  :decryption_keys,
16
- :encryption_keys
16
+ :encryption_keys,
17
+ :signature_name
17
18
 
18
19
  ###
19
20
  # Internal: Creates a settings file representing a path to a file on the
@@ -42,12 +43,13 @@ class File < Pathname
42
43
  # Chamber::File.new path: '/tmp/settings.yml'
43
44
  # # => <Chamber::File>
44
45
  #
45
- def initialize(options = {})
46
- self.namespaces = options[:namespaces] || {}
47
- self.decryption_keys = options[:decryption_keys] || {}
48
- self.encryption_keys = options[:encryption_keys] || {}
46
+ def initialize(path:, namespaces: {}, decryption_keys: {}, encryption_keys: {}, signature_name: nil)
47
+ self.namespaces = namespaces
48
+ self.decryption_keys = decryption_keys
49
+ self.encryption_keys = encryption_keys
50
+ self.signature_name = signature_name
49
51
 
50
- super options.fetch(:path)
52
+ super path
51
53
  end
52
54
 
53
55
  ###
@@ -70,13 +72,13 @@ class File < Pathname
70
72
  # ```
71
73
  #
72
74
  def to_settings
73
- @data ||= Settings.new(settings: file_contents_hash,
74
- namespaces: namespaces,
75
- decryption_keys: decryption_keys,
76
- encryption_keys: encryption_keys)
75
+ @to_settings ||= Settings.new(settings: file_contents_hash,
76
+ namespaces: namespaces,
77
+ decryption_keys: decryption_keys,
78
+ encryption_keys: encryption_keys)
77
79
  end
78
80
 
79
- # rubocop:disable Metrics/LineLength
81
+ # rubocop:disable Layout/LineLength, Metrics/AbcSize
80
82
  def secure
81
83
  insecure_settings = to_settings.insecure.to_flattened_name_hash
82
84
  secure_settings = to_settings.insecure.secure.to_flattened_name_hash
@@ -88,22 +90,22 @@ class File < Pathname
88
90
  escaped_name = Regexp.escape(name_pieces.last)
89
91
  escaped_value = Regexp.escape(value)
90
92
 
91
- file_contents.
92
- sub!(
93
+ file_contents
94
+ .sub!(
93
95
  /^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)['"]?#{escaped_value}['"]?$/,
94
96
  "\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
95
- )
97
+ )
96
98
 
97
- file_contents.
98
- sub!(
99
+ file_contents
100
+ .sub!(
99
101
  /^(\s*)#{secure_prefix_pattern}#{escaped_name}(\s*):(\s*)\|((?:\n\1\s{2}.*)+)/,
100
102
  "\\1#{secure_prefix}#{name_pieces.last}\\2:\\3#{secure_value}",
101
- )
103
+ )
102
104
  end
103
105
 
104
106
  write(file_contents)
105
107
  end
106
- # rubocop:enable Metrics/LineLength
108
+ # rubocop:enable Layout/LineLength, Metrics/AbcSize
107
109
 
108
110
  def sign
109
111
  signature_key_contents = decryption_keys[:signature]
@@ -111,7 +113,7 @@ class File < Pathname
111
113
  fail ArgumentError, 'You asked to sign your settings files but no signature key was found. Run `chamber init --signature` to generate one.' \
112
114
  unless signature_key_contents
113
115
 
114
- signature = Files::Signature.new(to_s, read, signature_key_contents)
116
+ signature = Files::Signature.new(to_s, read, signature_key_contents, signature_name)
115
117
 
116
118
  signature.write
117
119
  end
@@ -122,7 +124,7 @@ class File < Pathname
122
124
  fail ArgumentError, 'You asked to verify your settings files but no signature key was found. Run `chamber init --signature` to generate one.' \
123
125
  unless signature_key_contents
124
126
 
125
- signature = Files::Signature.new(to_s, read, signature_key_contents)
127
+ signature = Files::Signature.new(to_s, read, signature_key_contents, signature_name)
126
128
 
127
129
  signature.verify
128
130
  end