sidekiq-encrypted_args 2.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGE_LOG.md +18 -0
- data/README.md +9 -0
- data/VERSION +1 -1
- data/lib/sidekiq/encrypted_args/client_middleware.rb +7 -3
- data/lib/sidekiq/encrypted_args.rb +109 -29
- data/sidekiq-encrypted_args.gemspec +1 -2
- metadata +1 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 579403fba577dd83ce26ad13d8893ccdf86bc28883fff397fc029f265f0b7cd5
|
|
4
|
+
data.tar.gz: '08435986fca6664001ebb07dcd1b49a79a116b6cd78f26638a3c0a5d37e9c7b3'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 936274740450e2cbaf08f7f060df37f3a5a29e4aea47bc9985028403f2aca0f879f724167861e1b444f829b597682f2786dd2bf31f00b5ef5a26002ddda094d8
|
|
7
|
+
data.tar.gz: a59847233f9e50690fad84ed1cb071b204effe830f00bf738a08b2f7208211cd38d79e4b5504d1408c63dba930da4f87db31d0eb1d75c860b0dc1e2bc3bb53a9
|
data/CHANGE_LOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 2.0.2
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- An `ArgumentError` is now raised when a named encrypted argument cannot be matched to a parameter of the worker's `perform` method or when the worker class name cannot be resolved to a class. Previously these arguments were silently left unencrypted.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Setting the secret to an empty value (e.g. an empty array or empty string) no longer silently disables encryption. An `InvalidSecretError` is now raised when encrypting or decrypting if no secret is available.
|
|
16
|
+
- The client middleware no longer mutates the caller's arguments array in place when encrypting arguments.
|
|
17
|
+
- Lazily initializing the encryption keys from the `SIDEKIQ_ENCRYPTED_ARGS_SECRET` environment variable is now thread safe.
|
|
18
|
+
|
|
19
|
+
## 2.0.1
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Added initializer helper methods `Sidekiq::EncryptedArgs.encrypt_before` and `Sidekiq::EncryptedArgs.encrypt_after` to allow specifying middleware order for inserting the client encryption middleware and `Sidekiq::EncryptedArgs.decrypt_before` and `Sidekiq::EncryptedArgs.decrypt_after` to allow specifying middleware order for inserting the server decryption middleware.
|
|
24
|
+
|
|
7
25
|
## 2.0.0
|
|
8
26
|
|
|
9
27
|
### Changed
|
data/README.md
CHANGED
|
@@ -51,6 +51,15 @@ Sidekiq.configure_server do |config|
|
|
|
51
51
|
end
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
If there is specific middleware you want to insert the encryption or decryption middleware before or after, you can use the following helper methods:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
Sidekiq::EncryptedArgs.encrypt_before(SomeOtherMiddlewareClass)
|
|
58
|
+
Sidekiq::EncryptedArgs.encrypt_after(SomeOtherMiddlewareClass)
|
|
59
|
+
Sidekiq::EncryptedArgs.decrypt_before(SomeOtherMiddlewareClass)
|
|
60
|
+
Sidekiq::EncryptedArgs.decrypt_after(SomeOtherMiddlewareClass)
|
|
61
|
+
```
|
|
62
|
+
|
|
54
63
|
## Worker Configuration
|
|
55
64
|
|
|
56
65
|
To declare that a worker is using encrypted arguments, you must set the `encrypted_args` sidekiq option.
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0.
|
|
1
|
+
2.0.2
|
|
@@ -35,15 +35,19 @@ module Sidekiq
|
|
|
35
35
|
#
|
|
36
36
|
# Additionally, set `job["encrypted_args"]` to the canonicalized version (i.e. `Array<Integer>`)
|
|
37
37
|
#
|
|
38
|
+
# The args array is replaced rather than mutated in place since the array
|
|
39
|
+
# can be the same object passed in by the caller of Sidekiq::Client.push.
|
|
40
|
+
#
|
|
38
41
|
# @param [Hash] job The Sidekiq job hash containing arguments and metadata
|
|
39
42
|
# @param [Array<Integer>] encrypted_args array of indexes in job to encrypt
|
|
40
43
|
# @return [void]
|
|
41
44
|
def encrypt_job_arguments!(job, encrypted_args)
|
|
42
45
|
if encrypted_args
|
|
43
|
-
|
|
44
|
-
job_args.each_with_index do |value, position|
|
|
46
|
+
job["args"] = job["args"].map.with_index do |value, position|
|
|
45
47
|
if encrypted_args.include?(position) && !EncryptedArgs.encrypted?(value)
|
|
46
|
-
|
|
48
|
+
EncryptedArgs.encrypt(value)
|
|
49
|
+
else
|
|
50
|
+
value
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
job["encrypted_args"] = encrypted_args
|
|
@@ -12,6 +12,7 @@ module Sidekiq
|
|
|
12
12
|
# personally identifiable information.
|
|
13
13
|
module EncryptedArgs
|
|
14
14
|
@encryptors = nil
|
|
15
|
+
@mutex = Mutex.new
|
|
15
16
|
|
|
16
17
|
# Error thrown when the secret is invalid
|
|
17
18
|
class InvalidSecretError < StandardError
|
|
@@ -20,7 +21,8 @@ module Sidekiq
|
|
|
20
21
|
class << self
|
|
21
22
|
# Set the secret key used for encrypting arguments. If this is not set,
|
|
22
23
|
# the value will be loaded from the `SIDEKIQ_ENCRYPTED_ARGS_SECRET` environment
|
|
23
|
-
# variable. If that value is not set,
|
|
24
|
+
# variable. If that value is not set either, an InvalidSecretError will be
|
|
25
|
+
# raised when arguments are encrypted or decrypted.
|
|
24
26
|
#
|
|
25
27
|
# You can set multiple secrets by passing an array if you need to roll your secrets.
|
|
26
28
|
# The left most value in the array will be used as the encryption secret, but
|
|
@@ -38,7 +40,9 @@ module Sidekiq
|
|
|
38
40
|
# @param [String, Array<String>] value One or more secrets to use for encrypting arguments.
|
|
39
41
|
# @return [void]
|
|
40
42
|
def secret=(value)
|
|
41
|
-
@
|
|
43
|
+
@mutex.synchronize do
|
|
44
|
+
@encryptors = make_encryptors(value).freeze
|
|
45
|
+
end
|
|
42
46
|
end
|
|
43
47
|
|
|
44
48
|
# Add the client and server middleware to the default Sidekiq
|
|
@@ -75,6 +79,66 @@ module Sidekiq
|
|
|
75
79
|
end
|
|
76
80
|
end
|
|
77
81
|
|
|
82
|
+
# Insert the client encryption middleware before the specified middleware.
|
|
83
|
+
#
|
|
84
|
+
# @param [Class] middleware The middleware class to insert before
|
|
85
|
+
# @return [void]
|
|
86
|
+
def encrypt_before(middleware)
|
|
87
|
+
Sidekiq.configure_client do |config|
|
|
88
|
+
config.client_middleware do |chain|
|
|
89
|
+
chain.insert_before(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
Sidekiq.configure_server do |config|
|
|
94
|
+
config.client_middleware do |chain|
|
|
95
|
+
chain.insert_before(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Insert the client encryption middleware after the specified middleware.
|
|
101
|
+
#
|
|
102
|
+
# @param [Class] middleware The middleware class to insert after
|
|
103
|
+
# @return [void]
|
|
104
|
+
def encrypt_after(middleware)
|
|
105
|
+
Sidekiq.configure_client do |config|
|
|
106
|
+
config.client_middleware do |chain|
|
|
107
|
+
chain.insert_after(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
Sidekiq.configure_server do |config|
|
|
112
|
+
config.client_middleware do |chain|
|
|
113
|
+
chain.insert_after(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Insert the server decryption middleware before the specified middleware.
|
|
119
|
+
#
|
|
120
|
+
# @param [Class] middleware The middleware class to insert before
|
|
121
|
+
# @return [void]
|
|
122
|
+
def decrypt_before(middleware)
|
|
123
|
+
Sidekiq.configure_server do |config|
|
|
124
|
+
config.server_middleware do |chain|
|
|
125
|
+
chain.insert_before(middleware, Sidekiq::EncryptedArgs::ServerMiddleware)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Insert the server decryption middleware after the specified middleware.
|
|
131
|
+
#
|
|
132
|
+
# @param [Class] middleware The middleware class to insert after
|
|
133
|
+
# @return [void]
|
|
134
|
+
def decrypt_after(middleware)
|
|
135
|
+
Sidekiq.configure_server do |config|
|
|
136
|
+
config.server_middleware do |chain|
|
|
137
|
+
chain.insert_after(middleware, Sidekiq::EncryptedArgs::ServerMiddleware)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
78
142
|
# Encrypt a value.
|
|
79
143
|
#
|
|
80
144
|
# @example Encrypting a simple value
|
|
@@ -85,17 +149,12 @@ module Sidekiq
|
|
|
85
149
|
#
|
|
86
150
|
# @param [#to_json, Object] data Data to encrypt. You can pass any JSON compatible data types or structures.
|
|
87
151
|
#
|
|
88
|
-
# @return [String]
|
|
152
|
+
# @return [String, nil]
|
|
89
153
|
def encrypt(data)
|
|
90
154
|
return nil if data.nil?
|
|
91
155
|
|
|
92
156
|
json = (data.respond_to?(:to_json) ? data.to_json : JSON.generate(data))
|
|
93
|
-
|
|
94
|
-
if encrypted == json
|
|
95
|
-
data
|
|
96
|
-
else
|
|
97
|
-
encrypted
|
|
98
|
-
end
|
|
157
|
+
encrypt_string(json)
|
|
99
158
|
end
|
|
100
159
|
|
|
101
160
|
# Decrypt data
|
|
@@ -143,15 +202,24 @@ module Sidekiq
|
|
|
143
202
|
raise ArgumentError.new("Hash-based argument encryption is no longer supported.")
|
|
144
203
|
else
|
|
145
204
|
array_type = nil
|
|
146
|
-
Array(option).
|
|
205
|
+
Array(option).each do |val|
|
|
147
206
|
current_type = nil
|
|
148
207
|
if val.is_a?(Integer)
|
|
149
208
|
indexes << val
|
|
150
209
|
current_type = :integer
|
|
151
210
|
elsif val.is_a?(Symbol) || val.is_a?(String)
|
|
152
|
-
|
|
211
|
+
if worker_class.is_a?(String)
|
|
212
|
+
class_name = worker_class
|
|
213
|
+
worker_class = constantize(worker_class)
|
|
214
|
+
if worker_class.nil?
|
|
215
|
+
raise ArgumentError.new("Cannot resolve worker class #{class_name.inspect} to look up named encrypted args; use integer positions instead.")
|
|
216
|
+
end
|
|
217
|
+
end
|
|
153
218
|
position = perform_method_parameter_index(worker_class, val)
|
|
154
|
-
|
|
219
|
+
if position.nil?
|
|
220
|
+
raise ArgumentError.new("Encrypted arg #{val.inspect} is not a parameter of #{worker_class}#perform.")
|
|
221
|
+
end
|
|
222
|
+
indexes << position
|
|
155
223
|
current_type = :symbol
|
|
156
224
|
else
|
|
157
225
|
raise ArgumentError.new("Encrypted args must be specified as integers or symbols.")
|
|
@@ -174,10 +242,7 @@ module Sidekiq
|
|
|
174
242
|
private_constant :SALT
|
|
175
243
|
|
|
176
244
|
def encrypt_string(value)
|
|
177
|
-
|
|
178
|
-
return value if encryptor.nil?
|
|
179
|
-
|
|
180
|
-
encryptor.encrypt(value)
|
|
245
|
+
encryptors.first.encrypt(value)
|
|
181
246
|
end
|
|
182
247
|
|
|
183
248
|
def decrypt_string(value)
|
|
@@ -192,15 +257,20 @@ module Sidekiq
|
|
|
192
257
|
end
|
|
193
258
|
|
|
194
259
|
def encryptors
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
260
|
+
current = @encryptors
|
|
261
|
+
return current unless current.nil?
|
|
262
|
+
|
|
263
|
+
@mutex.synchronize do
|
|
264
|
+
if @encryptors.nil?
|
|
265
|
+
secret = ENV.fetch("SIDEKIQ_ENCRYPTED_ARGS_SECRET", "").strip
|
|
266
|
+
if secret.empty?
|
|
267
|
+
raise InvalidSecretError.new("Secret not set. Call Sidekiq::EncryptedArgs.secret= or set the SIDEKIQ_ENCRYPTED_ARGS_SECRET environment variable.")
|
|
268
|
+
end
|
|
200
269
|
|
|
201
|
-
|
|
270
|
+
@encryptors = make_encryptors(secret.split).freeze
|
|
271
|
+
end
|
|
272
|
+
@encryptors
|
|
202
273
|
end
|
|
203
|
-
@encryptors
|
|
204
274
|
end
|
|
205
275
|
|
|
206
276
|
# Create encryptors from secrets.
|
|
@@ -208,15 +278,16 @@ module Sidekiq
|
|
|
208
278
|
# @param [String, Array<String>] secrets One or more secrets to create encryptors from
|
|
209
279
|
# @return [Array<SecretKeys::Encryptor>, nil] Array of encryptors or nil if no secrets provided
|
|
210
280
|
def make_encryptors(secrets)
|
|
211
|
-
|
|
281
|
+
secrets = Array(secrets).reject { |val| val.nil? || val.to_s.empty? }
|
|
282
|
+
return nil if secrets.empty?
|
|
212
283
|
|
|
213
|
-
|
|
284
|
+
secrets.map { |val| SecretKeys::Encryptor.from_password(val, SALT) }
|
|
214
285
|
end
|
|
215
286
|
|
|
216
287
|
# Convert a string class name into the actual class constant.
|
|
217
288
|
#
|
|
218
289
|
# @param [String] class_name Name of a class (e.g., "MyModule::MyClass")
|
|
219
|
-
# @return [Class] The class constant that was referenced by name
|
|
290
|
+
# @return [Class, nil] The class constant that was referenced by name, or nil if it is not defined
|
|
220
291
|
def constantize(class_name)
|
|
221
292
|
names = class_name.split("::")
|
|
222
293
|
# Clear leading :: for root namespace since we're already calling from object
|
|
@@ -224,6 +295,8 @@ module Sidekiq
|
|
|
224
295
|
# Map reduce to the constant. Use inherit=false to not accidentally search
|
|
225
296
|
# parent modules
|
|
226
297
|
names.inject(Object) { |constant, name| constant.const_get(name, false) }
|
|
298
|
+
rescue NameError
|
|
299
|
+
nil
|
|
227
300
|
end
|
|
228
301
|
|
|
229
302
|
# Get the index of a parameter in the worker's perform method.
|
|
@@ -232,10 +305,17 @@ module Sidekiq
|
|
|
232
305
|
# @param [String, Symbol] parameter The parameter name to find
|
|
233
306
|
# @return [Integer, nil] The zero-based index of the parameter, or nil if not found
|
|
234
307
|
def perform_method_parameter_index(worker_class, parameter)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
308
|
+
return nil unless worker_class
|
|
309
|
+
|
|
310
|
+
perform_method = begin
|
|
311
|
+
worker_class.instance_method(:perform)
|
|
312
|
+
rescue NameError
|
|
313
|
+
nil
|
|
238
314
|
end
|
|
315
|
+
return nil if perform_method.nil?
|
|
316
|
+
|
|
317
|
+
parameter = parameter.to_sym
|
|
318
|
+
perform_method.parameters.find_index { |_, name| name == parameter }
|
|
239
319
|
end
|
|
240
320
|
end
|
|
241
321
|
end
|
|
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
19
19
|
ignore_files = %w[
|
|
20
20
|
.
|
|
21
|
+
benchmark.rb
|
|
21
22
|
Appraisals
|
|
22
23
|
Gemfile
|
|
23
24
|
Gemfile.lock
|
|
@@ -38,6 +39,4 @@ Gem::Specification.new do |spec|
|
|
|
38
39
|
|
|
39
40
|
spec.add_dependency "sidekiq", ">= 6.3"
|
|
40
41
|
spec.add_dependency "secret_keys"
|
|
41
|
-
|
|
42
|
-
spec.add_development_dependency "bundler"
|
|
43
42
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sidekiq-encrypted_args
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: bundler
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
41
|
email:
|
|
56
42
|
- bbdurand@gmail.com
|
|
57
43
|
- me@winstondurand.com
|