secret_keys 1.0.2 → 1.0.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +5 -3
- data/VERSION +1 -1
- data/lib/secret_keys/cli.rb +11 -4
- data/lib/secret_keys/encryptor.rb +6 -6
- data/lib/secret_keys.rb +89 -43
- data/secret_keys.gemspec +9 -5
- metadata +11 -15
- data/CHANGE_LOG.md +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd54d07d04fbd4695a6896fec897ee82171f27e10778aa59247dc2c216511149
|
|
4
|
+
data.tar.gz: 6faeb6462cce486f5f4e27619fe3e88e7f1587a1a51949821166ee3359419a0f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e509e3f5b8fb49e5c7859f18cd73a620ca89d95c0e82631cdb40bf77366d5dba131107d09d2057236d8e44982a6a232497c799e5723727968b37f6c88f253d13
|
|
7
|
+
data.tar.gz: 772f82950d3efe42d597bc6918f10f8ed2664d6b5738a9af0bb8767fb59b8212a0420a88a5169ad35972600936aace607323921b9bdf80dedf0b66669a7c6e22
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## 1.0.3
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Files are now written atomically (write to a temporary file and rename) so a crash or concurrent reader can never see a partially written secrets file. The mode of an existing file is preserved.
|
|
12
|
+
- New secrets files are now created readable and writable only by the owner (mode 0600) instead of relying on the process umask. The mode of an existing file is still preserved.
|
|
13
|
+
- `secret_keys init` now uses an exclusive file create so it can no longer overwrite a file created between the existence check and the write.
|
|
14
|
+
- `SecretKeys#save` no longer raises `ArgumentError` when passed a `Pathname`.
|
|
15
|
+
- An unsupported `.version` value that is a string now raises `SecretKeys::VersionError` instead of a comparison `ArgumentError`; numeric strings like `"1"` are accepted.
|
|
16
|
+
- Input that does not parse to a Hash now raises a clear `ArgumentError` instead of failing with confusing errors; input that parses to nothing (e.g. only comments) loads as an empty document.
|
|
17
|
+
- A non-Hash value in the `.encrypted` key now raises a clear `ArgumentError`.
|
|
18
|
+
- `SecretKeys#encryption_key=` now raises `SecretKeys::EncryptionKeyError` when given a nil or empty key instead of silently keeping the old key.
|
|
19
|
+
- The `--secret-key-file` CLI option now reports a missing file as a clean error instead of an unhandled `Errno::ENOENT`.
|
|
20
|
+
|
|
21
|
+
## 1.0.2
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Follow [RFC 4648](https://www.ietf.org/rfc/rfc4648.txt) base 64 encoding, removing line-feeds from the encoded data.
|
|
26
|
+
|
|
27
|
+
## 1.0.1 (June 01, 2020)
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fix missing documentation links
|
|
32
|
+
|
|
33
|
+
## v1.0.0 (May 31, 2020)
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Initial release
|
data/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Secret Keys
|
|
2
2
|
|
|
3
|
-
[](https://github.com/bdurand/secret_keys/actions/workflows/continuous_integration.yml)
|
|
4
|
+
[](https://github.com/bdurand/secret_keys/actions/workflows/regression_test.yml)
|
|
5
|
+
[](https://github.com/testdouble/standard)
|
|
5
6
|
[](https://badge.fury.io/rb/secret_keys)
|
|
7
|
+
[](https://badge.fury.io/rb/secret_keys)
|
|
6
8
|
|
|
7
9
|
This ruby gem handles encrypting values in a JSON or YAML file. It is yet another solution for storing secrets in a ruby project.
|
|
8
10
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.3
|
data/lib/secret_keys/cli.rb
CHANGED
|
@@ -78,6 +78,7 @@ module SecretKeys::CLI
|
|
|
78
78
|
HELP
|
|
79
79
|
opts.on("--secret-key-file=PATH", String, *secret_file_docs) do |value|
|
|
80
80
|
raise ArgumentError, "You have already passed in the secret key" unless @secret_key.nil?
|
|
81
|
+
raise ArgumentError, "Secret key file '#{value}' does not exist" unless File.exist?(value)
|
|
81
82
|
@secret_key = File.read(value).chomp
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -165,13 +166,19 @@ module SecretKeys::CLI
|
|
|
165
166
|
def run!
|
|
166
167
|
@secrets = SecretKeys.new({}, secret_key)
|
|
167
168
|
if input.is_a?(String)
|
|
168
|
-
|
|
169
|
+
contents = encrypted_file_contents
|
|
170
|
+
begin
|
|
171
|
+
# Open with exclusive create so a file created by another process
|
|
172
|
+
# between now and the write can never be clobbered. The file is
|
|
173
|
+
# created readable and writable only by the owner.
|
|
174
|
+
File.open(input, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |file|
|
|
175
|
+
file.write(contents)
|
|
176
|
+
end
|
|
177
|
+
rescue Errno::EEXIST
|
|
169
178
|
warn "Error: Cannot init preexisting file '#{input}'"
|
|
170
179
|
warn "You may want to try calling `secret_keys encrypt/edit` instead"
|
|
171
180
|
exit 1
|
|
172
181
|
end
|
|
173
|
-
|
|
174
|
-
File.write(input, encrypted_file_contents)
|
|
175
182
|
else
|
|
176
183
|
$stdout.write(encrypted_file_contents)
|
|
177
184
|
end
|
|
@@ -220,7 +227,7 @@ module SecretKeys::CLI
|
|
|
220
227
|
raise ArgumentError, "Cannot perform in place editing on streams" unless @input.is_a?(String)
|
|
221
228
|
# make sure we read the file **before** writing to it.
|
|
222
229
|
contents = encrypted_file_contents
|
|
223
|
-
|
|
230
|
+
SecretKeys.atomic_write(@input, contents)
|
|
224
231
|
else
|
|
225
232
|
$stdout.write(encrypted_file_contents)
|
|
226
233
|
$stdout.flush
|
|
@@ -21,8 +21,8 @@ class SecretKeys::Encryptor
|
|
|
21
21
|
class << self
|
|
22
22
|
# Create an Encryptor from a password and salt. This is a shortcut for generating an Encryptor
|
|
23
23
|
# with a 32 byte encryption key. The key will be derived from the password and salt.
|
|
24
|
-
# @param [String]
|
|
25
|
-
# @param [String]
|
|
24
|
+
# @param password [String] secret used to encrypt the data
|
|
25
|
+
# @param salt [String] random hex-encoded byte array for key derivation
|
|
26
26
|
# @return [SecretKeys::Encryptor] a new encryptor with key derived from password and salt
|
|
27
27
|
def from_password(password, salt)
|
|
28
28
|
raise ArgumentError, "Password must be present" if password.nil? || password.empty?
|
|
@@ -55,7 +55,7 @@ class SecretKeys::Encryptor
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# @param [String]
|
|
58
|
+
# @param raw_key [String] the key directly passed into the encrypt/decrypt functions. This must be exactly {KEY_LENGTH} bytes long.
|
|
59
59
|
def initialize(raw_key)
|
|
60
60
|
raise ArgumentError, "key must be #{KEY_LENGTH} bytes long" unless raw_key.bytesize == KEY_LENGTH
|
|
61
61
|
@derived_key = raw_key
|
|
@@ -65,7 +65,7 @@ class SecretKeys::Encryptor
|
|
|
65
65
|
# calling this function multiple times will result in different values. Only strings
|
|
66
66
|
# can be encrypted. Any other object type will be return the value passed in.
|
|
67
67
|
#
|
|
68
|
-
# @param [String]
|
|
68
|
+
# @param str [String] string to encrypt (assumes UTF-8)
|
|
69
69
|
# @return [String] Base64 encoded encrypted string with all aes parameters
|
|
70
70
|
def encrypt(str)
|
|
71
71
|
return str unless str.is_a?(String)
|
|
@@ -96,13 +96,13 @@ class SecretKeys::Encryptor
|
|
|
96
96
|
# Decrypt a string with the encryption key. If the value is not a string or it was
|
|
97
97
|
# not encrypted with the encryption key, the value itself will be returned.
|
|
98
98
|
#
|
|
99
|
-
# @param [String]
|
|
99
|
+
# @param encrypted_str [String] Base64 encoded encrypted string with aes params (from {#encrypt})
|
|
100
100
|
# @return [String] decrypted string value
|
|
101
101
|
# @raise [OpenSSL::Cipher::CipherError] there is something wrong with the encoded data (usually incorrect key)
|
|
102
102
|
def decrypt(encrypted_str)
|
|
103
103
|
return encrypted_str unless self.class.encrypted?(encrypted_str)
|
|
104
104
|
|
|
105
|
-
decrypt_str = encrypted_str[ENCRYPTED_PREFIX.length
|
|
105
|
+
decrypt_str = encrypted_str[ENCRYPTED_PREFIX.length..]
|
|
106
106
|
params = decode_aes(decrypt_str)
|
|
107
107
|
|
|
108
108
|
cipher = OpenSSL::Cipher.new(CIPHER).decrypt
|
data/lib/secret_keys.rb
CHANGED
|
@@ -15,13 +15,42 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
15
15
|
# Error if the crypto version specified in the file is unsupported
|
|
16
16
|
class VersionError < StandardError; end
|
|
17
17
|
|
|
18
|
+
# Write a file atomically by writing to a temporary file in the same directory and
|
|
19
|
+
# renaming it over the target path so that readers never see a partially written file.
|
|
20
|
+
# The mode of an existing file at the path will be preserved. New files are created
|
|
21
|
+
# readable and writable only by the owner.
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
24
|
+
# @param path [String, Pathname] path of the file to write
|
|
25
|
+
# @param content [String] contents of the file
|
|
26
|
+
# @return [void]
|
|
27
|
+
def self.atomic_write(path, content)
|
|
28
|
+
path = File.expand_path(path.to_s)
|
|
29
|
+
mode = begin
|
|
30
|
+
File.stat(path).mode & 0o7777
|
|
31
|
+
rescue Errno::ENOENT
|
|
32
|
+
0o600
|
|
33
|
+
end
|
|
34
|
+
tmp_path = File.join(File.dirname(path), ".#{File.basename(path)}.tmp.#{Process.pid}.#{rand(1_000_000)}")
|
|
35
|
+
begin
|
|
36
|
+
File.open(tmp_path, "w") do |file| # rubocop:disable Style/FileWrite
|
|
37
|
+
file.write(content)
|
|
38
|
+
end
|
|
39
|
+
File.chmod(mode, tmp_path)
|
|
40
|
+
File.rename(tmp_path, path)
|
|
41
|
+
ensure
|
|
42
|
+
File.unlink(tmp_path) if File.exist?(tmp_path)
|
|
43
|
+
end
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
18
47
|
# Parse a JSON or YAML stream or file with encrypted values. Any values in the ".encrypted" key
|
|
19
48
|
# in the document will be decrypted with the provided encryption key. If values
|
|
20
49
|
# were put into the ".encrypted" key manually and are not yet encrypted, they will be used
|
|
21
50
|
# as is without any decryption.
|
|
22
51
|
#
|
|
23
|
-
# @param [String, #read, Hash]
|
|
24
|
-
# @param [String]
|
|
52
|
+
# @param path_or_stream [String, #read, Hash] path to a JSON/YAML file to load, an IO object, or a Hash (mostly for testing purposes)
|
|
53
|
+
# @param encryption_key [String] secret to use for encryption/decryption
|
|
25
54
|
#
|
|
26
55
|
# @note If no encryption key is passed, this will defautl to env var SECRET_KEYS_ENCRYPTION_KEY
|
|
27
56
|
# or (if that is empty) the value read from the file path in SECRET_KEYS_ENCRYPTION_KEY_FILE.
|
|
@@ -48,7 +77,7 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
48
77
|
|
|
49
78
|
# Mark the key as being encrypted when the JSON is saved.
|
|
50
79
|
#
|
|
51
|
-
# @param [String] key
|
|
80
|
+
# @param key [String] key to mark as needing encryption
|
|
52
81
|
# @return [void]
|
|
53
82
|
def encrypt!(key)
|
|
54
83
|
@secret_keys << key
|
|
@@ -57,7 +86,7 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
57
86
|
|
|
58
87
|
# Mark the key as no longer being decrypted when the JSON is saved.
|
|
59
88
|
#
|
|
60
|
-
# @param [String] key
|
|
89
|
+
# @param key [String] key to mark as not needing encryption
|
|
61
90
|
# @return [void]
|
|
62
91
|
def decrypt!(key)
|
|
63
92
|
@secret_keys.delete(key)
|
|
@@ -66,7 +95,7 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
66
95
|
|
|
67
96
|
# Return true if the key is encrypted.
|
|
68
97
|
#
|
|
69
|
-
# @param [String] key
|
|
98
|
+
# @param key [String] key to check
|
|
70
99
|
# @return [Boolean]
|
|
71
100
|
def encrypted?(key)
|
|
72
101
|
@secret_keys.include?(key)
|
|
@@ -77,8 +106,8 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
77
106
|
# different initialization vector). This can be helpful if you have your secrets in source
|
|
78
107
|
# control so that only changed keys will actually be changed in the file when it is updated.
|
|
79
108
|
#
|
|
80
|
-
# @param [String, Pathname] path
|
|
81
|
-
# @param [String, Symbol]
|
|
109
|
+
# @param path [String, Pathname] path of the file to save. If the file exists, only changed values will be updated.
|
|
110
|
+
# @param format [String, Symbol] output format (YAML or JSON) to use. This will default based on the extension on the file path or the format originally used
|
|
82
111
|
# @return [void]
|
|
83
112
|
def save(path, format: nil)
|
|
84
113
|
# create a copy of the encrypted hash for working on
|
|
@@ -96,7 +125,7 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
96
125
|
|
|
97
126
|
output = ((format == "yaml") ? YAML.dump(encrypted) : JSON.pretty_generate(encrypted))
|
|
98
127
|
output << $/ unless output.end_with?($/) # ensure file ends with system dependent new line
|
|
99
|
-
|
|
128
|
+
self.class.atomic_write(path, output)
|
|
100
129
|
nil
|
|
101
130
|
end
|
|
102
131
|
|
|
@@ -131,9 +160,10 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
131
160
|
|
|
132
161
|
# Change the encryption key in the document. When saving later, this key will be used.
|
|
133
162
|
#
|
|
134
|
-
# @param [String]
|
|
163
|
+
# @param new_encryption_key [String] encryption key to use for future {#save} calls
|
|
135
164
|
# @return [void]
|
|
136
165
|
def encryption_key=(new_encryption_key)
|
|
166
|
+
raise EncryptionKeyError.new("Encryption key not specified") if new_encryption_key.nil? || new_encryption_key.empty?
|
|
137
167
|
@original_encrypted = {}
|
|
138
168
|
update_secret(key: new_encryption_key)
|
|
139
169
|
end
|
|
@@ -166,10 +196,10 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
166
196
|
|
|
167
197
|
hash = {}
|
|
168
198
|
if path_or_stream.is_a?(Hash)
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
hash =
|
|
199
|
+
# Deep copy the hash. Otherwise, we can end up using destructive `#delete`
|
|
200
|
+
# operations and mess up deeply nested values for external code
|
|
201
|
+
# (esp. when loading key: .encrypted)
|
|
202
|
+
hash = deep_dup(path_or_stream)
|
|
173
203
|
elsif path_or_stream
|
|
174
204
|
data = path_or_stream.read
|
|
175
205
|
hash = parse_data(data)
|
|
@@ -177,20 +207,31 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
177
207
|
|
|
178
208
|
encrypted_values = hash.delete(ENCRYPTED)
|
|
179
209
|
if encrypted_values
|
|
180
|
-
|
|
210
|
+
raise ArgumentError, "The #{ENCRYPTED} key must contain a Hash of encrypted values" unless encrypted_values.is_a?(Hash)
|
|
211
|
+
|
|
212
|
+
@original_encrypted = deep_dup(encrypted_values)
|
|
181
213
|
|
|
182
214
|
version = encrypted_values.delete(VERSION_KEY) || CRYPTO_VERSION
|
|
183
|
-
|
|
215
|
+
if version.is_a?(String)
|
|
216
|
+
version = begin
|
|
217
|
+
Integer(version, 10)
|
|
218
|
+
rescue ArgumentError
|
|
219
|
+
version
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
unless version.is_a?(Numeric) && version <= CRYPTO_VERSION
|
|
223
|
+
raise VersionError, "Unsupported file version #{version}. Max supported is #{CRYPTO_VERSION}."
|
|
224
|
+
end
|
|
184
225
|
|
|
185
226
|
file_key = encrypted_values.delete(ENCRYPTION_KEY)
|
|
186
|
-
salt =
|
|
227
|
+
salt = encrypted_values.delete(SALT) || Encryptor.random_salt
|
|
187
228
|
update_secret(salt: salt)
|
|
188
229
|
|
|
189
230
|
# Check that we are using the right key
|
|
190
231
|
if file_key && !encryption_key_matches?(file_key)
|
|
191
232
|
raise EncryptionKeyError.new("Incorrect encryption key")
|
|
192
233
|
end
|
|
193
|
-
@secret_keys = encrypted_values.keys
|
|
234
|
+
@secret_keys = Set.new(encrypted_values.keys)
|
|
194
235
|
hash.merge!(decrypt_values(encrypted_values))
|
|
195
236
|
elsif @salt.nil?
|
|
196
237
|
# if no salt exists, create one.
|
|
@@ -201,15 +242,38 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
201
242
|
end
|
|
202
243
|
|
|
203
244
|
# Attempt to parse the file first using JSON and fallback to YAML
|
|
204
|
-
# @param [String]
|
|
245
|
+
# @param data [String] file data to parse
|
|
205
246
|
# @return [Hash] data parsed to a hash
|
|
206
247
|
def parse_data(data)
|
|
207
248
|
@format = :json
|
|
208
249
|
return {} if data.nil? || data.empty?
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
250
|
+
hash = begin
|
|
251
|
+
JSON.parse(data)
|
|
252
|
+
rescue JSON::JSONError
|
|
253
|
+
@format = :yaml
|
|
254
|
+
YAML.safe_load(data)
|
|
255
|
+
end
|
|
256
|
+
# YAML.safe_load returns false instead of nil for empty documents on Ruby < 3.4.
|
|
257
|
+
return {} if hash.nil? || hash == false
|
|
258
|
+
raise ArgumentError, "Data must parse to a Hash of key value pairs" unless hash.is_a?(Hash)
|
|
259
|
+
hash
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Deep copy a parsed data structure of hashes, arrays, and scalar values.
|
|
263
|
+
def deep_dup(value)
|
|
264
|
+
if value.is_a?(Hash)
|
|
265
|
+
copy = {}
|
|
266
|
+
value.each do |key, val|
|
|
267
|
+
copy[deep_dup(key)] = deep_dup(val)
|
|
268
|
+
end
|
|
269
|
+
copy
|
|
270
|
+
elsif value.is_a?(Array)
|
|
271
|
+
value.collect { |val| deep_dup(val) }
|
|
272
|
+
elsif value.is_a?(String)
|
|
273
|
+
value.dup
|
|
274
|
+
else
|
|
275
|
+
value
|
|
276
|
+
end
|
|
213
277
|
end
|
|
214
278
|
|
|
215
279
|
# Recursively encrypt all values.
|
|
@@ -263,23 +327,6 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
263
327
|
@encryptor.decrypt(encrypted_value)
|
|
264
328
|
end
|
|
265
329
|
|
|
266
|
-
# Helper method to test if two values are both encrypted, but result in the same decrypted value.
|
|
267
|
-
def equal_encrypted_values?(value_1, value_2)
|
|
268
|
-
return true if value_1 == value_2
|
|
269
|
-
|
|
270
|
-
decrypt_val_1 = decrypt_value(value_1)
|
|
271
|
-
decrypt_val_2 = decrypt_value(value_2)
|
|
272
|
-
if decrypt_val_1 == decrypt_val_2
|
|
273
|
-
if value_1 == decrypt_val_1 || value_2 == decrypt_val_2
|
|
274
|
-
false
|
|
275
|
-
else
|
|
276
|
-
true
|
|
277
|
-
end
|
|
278
|
-
else
|
|
279
|
-
false
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
|
|
283
330
|
# This is an encrypted known value that can be used determine if the secret has changed.
|
|
284
331
|
def encrypted_known_value
|
|
285
332
|
encrypt_value(KNOWN_VALUE)
|
|
@@ -294,13 +341,12 @@ class SecretKeys < DelegateClass(Hash)
|
|
|
294
341
|
end
|
|
295
342
|
|
|
296
343
|
def yaml_file?(path)
|
|
297
|
-
ext =
|
|
298
|
-
ext == "yaml" || ext == "yml"
|
|
344
|
+
ext = File.extname(path.to_s).downcase
|
|
345
|
+
ext == ".yaml" || ext == ".yml"
|
|
299
346
|
end
|
|
300
347
|
|
|
301
348
|
def json_file?(path)
|
|
302
|
-
|
|
303
|
-
ext == "json"
|
|
349
|
+
File.extname(path.to_s).downcase == ".json"
|
|
304
350
|
end
|
|
305
351
|
|
|
306
352
|
# Update the secret key by updating the salt
|
data/secret_keys.gemspec
CHANGED
|
@@ -7,9 +7,12 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.summary = "Simple mechanism for loading JSON file with encrypted values."
|
|
8
8
|
spec.homepage = "https://github.com/bdurand/secret_keys"
|
|
9
9
|
spec.license = "MIT"
|
|
10
|
-
|
|
11
|
-
spec.metadata
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
spec.metadata = {
|
|
12
|
+
"homepage_uri" => spec.homepage,
|
|
13
|
+
"source_code_uri" => spec.homepage,
|
|
14
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
15
|
+
}
|
|
13
16
|
|
|
14
17
|
# Specify which files should be added to the gem when it is released.
|
|
15
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
@@ -17,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
|
17
20
|
.
|
|
18
21
|
Appraisals
|
|
19
22
|
Gemfile
|
|
23
|
+
Gemfile.lock
|
|
20
24
|
Rakefile
|
|
21
25
|
gemfiles/
|
|
22
26
|
spec/
|
|
@@ -29,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
|
29
33
|
spec.bindir = "bin"
|
|
30
34
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
31
35
|
|
|
32
|
-
spec.required_ruby_version = ">= 2.
|
|
36
|
+
spec.required_ruby_version = ">= 2.6"
|
|
33
37
|
|
|
34
|
-
spec.add_development_dependency "bundler"
|
|
38
|
+
spec.add_development_dependency "bundler"
|
|
35
39
|
end
|
metadata
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: secret_keys
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
8
8
|
- Winston Durand
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: bundler
|
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
|
17
16
|
requirements:
|
|
18
|
-
- - "
|
|
17
|
+
- - ">="
|
|
19
18
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '
|
|
19
|
+
version: '0'
|
|
21
20
|
type: :development
|
|
22
21
|
prerelease: false
|
|
23
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
23
|
requirements:
|
|
25
|
-
- - "
|
|
24
|
+
- - ">="
|
|
26
25
|
- !ruby/object:Gem::Version
|
|
27
|
-
version: '
|
|
28
|
-
description:
|
|
26
|
+
version: '0'
|
|
29
27
|
email:
|
|
30
28
|
- bbdurand@gmail.com
|
|
31
29
|
- me@winstondurand.com
|
|
@@ -34,7 +32,7 @@ executables:
|
|
|
34
32
|
extensions: []
|
|
35
33
|
extra_rdoc_files: []
|
|
36
34
|
files:
|
|
37
|
-
-
|
|
35
|
+
- CHANGELOG.md
|
|
38
36
|
- MIT_LICENSE.txt
|
|
39
37
|
- README.md
|
|
40
38
|
- VERSION
|
|
@@ -49,9 +47,8 @@ licenses:
|
|
|
49
47
|
- MIT
|
|
50
48
|
metadata:
|
|
51
49
|
homepage_uri: https://github.com/bdurand/secret_keys
|
|
52
|
-
source_code_uri: https://github.com/bdurand/secret_keys
|
|
53
|
-
|
|
54
|
-
post_install_message:
|
|
50
|
+
source_code_uri: https://github.com/bdurand/secret_keys
|
|
51
|
+
changelog_uri: https://github.com/bdurand/secret_keys/blob/main/CHANGELOG.md
|
|
55
52
|
rdoc_options: []
|
|
56
53
|
require_paths:
|
|
57
54
|
- lib
|
|
@@ -59,15 +56,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
59
56
|
requirements:
|
|
60
57
|
- - ">="
|
|
61
58
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '2.
|
|
59
|
+
version: '2.6'
|
|
63
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
61
|
requirements:
|
|
65
62
|
- - ">="
|
|
66
63
|
- !ruby/object:Gem::Version
|
|
67
64
|
version: '0'
|
|
68
65
|
requirements: []
|
|
69
|
-
rubygems_version:
|
|
70
|
-
signing_key:
|
|
66
|
+
rubygems_version: 4.0.3
|
|
71
67
|
specification_version: 4
|
|
72
68
|
summary: Simple mechanism for loading JSON file with encrypted values.
|
|
73
69
|
test_files: []
|
data/CHANGE_LOG.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [1.0.2] - 2023-04-04
|
|
9
|
-
|
|
10
|
-
- Follow [RFC 4648](https://www.ietf.org/rfc/rfc4648.txt) base 64 encoding, removing line-feeds from the encoded data.
|
|
11
|
-
|
|
12
|
-
## [1.0.1] - 2020-06-01
|
|
13
|
-
|
|
14
|
-
### Fixed
|
|
15
|
-
|
|
16
|
-
- Fix missing documentation links
|
|
17
|
-
|
|
18
|
-
## [1.0.0] 2020-05-31
|
|
19
|
-
|
|
20
|
-
### Added
|
|
21
|
-
|
|
22
|
-
- Initial release
|