Module: Sidekiq::EncryptedArgs

Defined in:
lib/sidekiq/encrypted_args.rb,
lib/sidekiq/encrypted_args/version.rb,
lib/sidekiq/encrypted_args/client_middleware.rb,
lib/sidekiq/encrypted_args/server_middleware.rb

Overview

Provides middleware for encrypting sensitive arguments in Sidekiq jobs.

This module allows you to specify which job arguments should be encrypted in Redis to protect sensitive information like API keys, passwords, or personally identifiable information.

Defined Under Namespace

Classes: ClientMiddleware, InvalidSecretError, ServerMiddleware

Constant Summary collapse

VERSION =

The current version of the sidekiq-encrypted_args gem.

File.read(File.join(__dir__, "..", "..", "..", "VERSION")).chomp.freeze

Class Method Summary collapse

Class Method Details

.configure!(secret: nil) ⇒ Object

Add the client and server middleware to the default Sidekiq middleware chains. If you need to ensure the order of where the middleware is added, you can forgo this method and add it yourself.

This method prepends client middleware and appends server middleware.

Examples:

Basic configuration

Sidekiq::EncryptedArgs.configure!(secret: "your_secret_key")

Configuration using environment variable

ENV['SIDEKIQ_ENCRYPTED_ARGS_SECRET'] = "your_secret_key"
Sidekiq::EncryptedArgs.configure!

Parameters:

  • secret (String) (defaults to: nil)

    optionally set the secret here. See secret=



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/sidekiq/encrypted_args.rb', line 62

def configure!(secret: nil)
  self.secret = secret unless secret.nil?
  encryptors # Calling encryptors will validate that a secret is set.

  Sidekiq.configure_client do |config|
    config.client_middleware do |chain|
      chain.prepend Sidekiq::EncryptedArgs::ClientMiddleware
    end
  end

  Sidekiq.configure_server do |config|
    config.server_middleware do |chain|
      chain.add Sidekiq::EncryptedArgs::ServerMiddleware
    end
    config.client_middleware do |chain|
      chain.prepend Sidekiq::EncryptedArgs::ClientMiddleware
    end
  end
end

.decrypt(encrypted_data) ⇒ Object

Decrypt data

an unencrypted string, then the string itself will be returned.

Examples:

Decrypting an encrypted value

EncryptedArgs.decrypt("encrypted_string") #=> "original_value"

Handling unencrypted data

EncryptedArgs.decrypt("unencrypted_string") #=> "unencrypted_string"

Parameters:

  • encrypted_data (String)

    Data that was previously encrypted. If the value passed in is

Returns:

  • (Object)


172
173
174
175
176
# File 'lib/sidekiq/encrypted_args.rb', line 172

def decrypt(encrypted_data)
  return encrypted_data unless SecretKeys::Encryptor.encrypted?(encrypted_data)
  json = decrypt_string(encrypted_data)
  JSON.parse(json)
end

.decrypt_after(middleware) ⇒ void

This method returns an undefined value.

Insert the server decryption middleware after the specified middleware.

Parameters:

  • middleware (Class)

    The middleware class to insert after



134
135
136
137
138
139
140
# File 'lib/sidekiq/encrypted_args.rb', line 134

def decrypt_after(middleware)
  Sidekiq.configure_server do |config|
    config.server_middleware do |chain|
      chain.insert_after(middleware, Sidekiq::EncryptedArgs::ServerMiddleware)
    end
  end
end

.decrypt_before(middleware) ⇒ void

This method returns an undefined value.

Insert the server decryption middleware before the specified middleware.

Parameters:

  • middleware (Class)

    The middleware class to insert before



122
123
124
125
126
127
128
# File 'lib/sidekiq/encrypted_args.rb', line 122

def decrypt_before(middleware)
  Sidekiq.configure_server do |config|
    config.server_middleware do |chain|
      chain.insert_before(middleware, Sidekiq::EncryptedArgs::ServerMiddleware)
    end
  end
end

.encrypt(data) ⇒ String?

Encrypt a value.

Examples:

Encrypting a simple value

EncryptedArgs.encrypt("secret_value") #=> "encrypted_string"

Encrypting complex data

EncryptedArgs.encrypt({api_key: "secret", user_id: 123}) #=> "encrypted_string"

Parameters:

  • data (#to_json, Object)

    Data to encrypt. You can pass any JSON compatible data types or structures.

Returns:

  • (String, nil)


153
154
155
156
157
158
# File 'lib/sidekiq/encrypted_args.rb', line 153

def encrypt(data)
  return nil if data.nil?

  json = (data.respond_to?(:to_json) ? data.to_json : JSON.generate(data))
  encrypt_string(json)
end

.encrypt_after(middleware) ⇒ void

This method returns an undefined value.

Insert the client encryption middleware after the specified middleware.

Parameters:

  • middleware (Class)

    The middleware class to insert after



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/sidekiq/encrypted_args.rb', line 104

def encrypt_after(middleware)
  Sidekiq.configure_client do |config|
    config.client_middleware do |chain|
      chain.insert_after(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
    end
  end

  Sidekiq.configure_server do |config|
    config.client_middleware do |chain|
      chain.insert_after(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
    end
  end
end

.encrypt_before(middleware) ⇒ void

This method returns an undefined value.

Insert the client encryption middleware before the specified middleware.

Parameters:

  • middleware (Class)

    The middleware class to insert before



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/sidekiq/encrypted_args.rb', line 86

def encrypt_before(middleware)
  Sidekiq.configure_client do |config|
    config.client_middleware do |chain|
      chain.insert_before(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
    end
  end

  Sidekiq.configure_server do |config|
    config.client_middleware do |chain|
      chain.insert_before(middleware, Sidekiq::EncryptedArgs::ClientMiddleware)
    end
  end
end

.encrypted?(value) ⇒ Boolean

Check if a value is encrypted.

Returns:

  • (Boolean)


181
182
183
# File 'lib/sidekiq/encrypted_args.rb', line 181

def encrypted?(value)
  SecretKeys::Encryptor.encrypted?(value)
end

.encrypted_args_option(worker_class, job) ⇒ Array<Integer>?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Private helper method to get the encrypted args option from an options hash. The value of this option can be true or an array indicating if each positional argument should be encrypted, or a hash with keys for the argument position and true as the value.

Parameters:

  • worker_class (String, Class)

    The worker class or class name

  • job (Hash)

    The Sidekiq job hash containing arguments and metadata

Returns:

  • (Array<Integer>, nil)

    Array of argument positions to encrypt, or nil if encryption is not configured



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/sidekiq/encrypted_args.rb', line 193

def encrypted_args_option(worker_class, job)
  option = job["encrypted_args"]
  return nil if option.nil?
  return [] if option == false

  indexes = []
  if option == true
    job["args"].size.times { |i| indexes << i }
  elsif option.is_a?(Hash)
    raise ArgumentError.new("Hash-based argument encryption is no longer supported.")
  else
    array_type = nil
    Array(option).each do |val|
      current_type = nil
      if val.is_a?(Integer)
        indexes << val
        current_type = :integer
      elsif val.is_a?(Symbol) || val.is_a?(String)
        if worker_class.is_a?(String)
          class_name = worker_class
          worker_class = constantize(worker_class)
          if worker_class.nil?
            raise ArgumentError.new("Cannot resolve worker class #{class_name.inspect} to look up named encrypted args; use integer positions instead.")
          end
        end
        position = perform_method_parameter_index(worker_class, val)
        if position.nil?
          raise ArgumentError.new("Encrypted arg #{val.inspect} is not a parameter of #{worker_class}#perform.")
        end
        indexes << position
        current_type = :symbol
      else
        raise ArgumentError.new("Encrypted args must be specified as integers or symbols.")
      end

      if array_type && current_type != array_type
        raise ArgumentError.new("Encrypted args cannot mix integers and symbols.")
      else
        array_type ||= current_type
      end
    end
  end
  indexes
end

.secret=(value) ⇒ void

This method returns an undefined value.

Set the secret key used for encrypting arguments. If this is not set, the value will be loaded from the SIDEKIQ_ENCRYPTED_ARGS_SECRET environment variable. If that value is not set either, an InvalidSecretError will be raised when arguments are encrypted or decrypted.

You can set multiple secrets by passing an array if you need to roll your secrets. The left most value in the array will be used as the encryption secret, but all the values will be tried when decrypting. That way if you have scheduled jobs that were encrypted with a different secret, you can still make it available when decrypting the arguments when the job gets run. If you are using the environment variable, separate the keys with spaces.

Examples:

Setting a single secret

Sidekiq::EncryptedArgs.secret = "your_secret_key"

Rolling secrets (multiple keys for backward compatibility)

Sidekiq::EncryptedArgs.secret = ["new_secret", "old_secret", "older_secret"]

Parameters:

  • value (String, Array<String>)

    One or more secrets to use for encrypting arguments.



42
43
44
45
46
# File 'lib/sidekiq/encrypted_args.rb', line 42

def secret=(value)
  @mutex.synchronize do
    @encryptors = make_encryptors(value).freeze
  end
end