module Vault::Rails
Constants
- DEFAULT_ENCODING
The default encoding.
@return [String]
- DEV_PREFIX
- DEV_WARNING
The warning string to print when running in development mode.
- SERIALIZERS
The list of serializers.
@return [Hash<Symbol, Module>]
- VERSION
Attributes
API client object based off the configured options in {Configurable}.
@return [Vault::Client]
Public Class Methods
Decrypt the given ciphertext data using the provided mount and key.
@param [String] path
the mount point
@param [String] key
the key to decrypt at
@param [String] ciphertext
the ciphertext to decrypt
@param [Vault::Client] client
the Vault client to use
@return [String]
the decrypted plaintext text
# File lib/vault/rails.rb, line 111 def decrypt(path, key, ciphertext, client: self.client, context: nil) if ciphertext.blank? return ciphertext end path = path.to_s if !path.is_a?(String) key = key.to_s if !key.is_a?(String) with_retries do if self.enabled? result = self.vault_decrypt(path, key, ciphertext, client: client, context: context) else result = self.memory_decrypt(path, key, ciphertext, client: client, context: context) end return self.force_encoding(result) end end
Encrypt the given plaintext data using the provided mount and key.
@param [String] path
the mount point
@param [String] key
the key to encrypt at
@param [String] plaintext
the plaintext to encrypt
@param [Vault::Client] client
the Vault client to use
@return [String]
the encrypted cipher text
# File lib/vault/rails.rb, line 79 def encrypt(path, key, plaintext, client: self.client, context: nil) if plaintext.blank? return plaintext end path = path.to_s if !path.is_a?(String) key = key.to_s if !key.is_a?(String) with_retries do if self.enabled? result = self.vault_encrypt(path, key, plaintext, client: client, context: context) else result = self.memory_encrypt(path, key, plaintext, client: client, context: context) end return self.force_encoding(result) end end
Delegate all methods to the client object, essentially making the module object behave like a {Vault::Client}.
# File lib/vault/rails.rb, line 53 def method_missing(m, *args, &block) if client.respond_to?(m) client.public_send(m, *args, &block) else super end end
Delegating ‘respond_to` to the {Vault::Client}.
# File lib/vault/rails.rb, line 62 def respond_to_missing?(m, include_private = false) client.respond_to?(m, include_private) || super end
Get the serializer that corresponds to the given key. If the key does not correspond to a known serializer, an exception will be raised.
@param [#to_sym] key
the name of the serializer
@return [~Serializer]
# File lib/vault/rails.rb, line 137 def serializer_for(key) key = key.to_sym if !key.is_a?(Symbol) if serializer = SERIALIZERS[key] return serializer else raise Vault::Rails::UnknownSerializerError.new(key) end end
# File lib/vault/rails.rb, line 40 def setup! Vault.setup! @client = Vault.client @client.class.instance_eval do include Vault::Rails::Configurable end self end
# File lib/vault/rails.rb, line 160 def transform_decode(ciphertext, opts={}) return ciphertext if ciphertext&.empty? request_opts = {} request_opts[:value] = ciphertext if opts[:transformation] request_opts[:transformation] = opts[:transformation] end role_name = transform_role_name(opts) puts request_opts client.transform.decode(role_name: role_name, **request_opts) end
# File lib/vault/rails.rb, line 147 def transform_encode(plaintext, opts={}) return plaintext if plaintext&.empty? request_opts = {} request_opts[:value] = plaintext if opts[:transformation] request_opts[:transformation] = opts[:transformation] end role_name = transform_role_name(opts) client.transform.encode(role_name: role_name, **request_opts) end
Protected Class Methods
Forces the encoding into the default Rails
encoding and returns the newly encoded string. @return [String]
# File lib/vault/rails.rb, line 247 def force_encoding(str) encoding = ::Rails.application.config.encoding || DEFAULT_ENCODING str.force_encoding(encoding).encode(encoding) end
Perform in-memory decryption. This is useful for testing and development.
# File lib/vault/rails.rb, line 190 def memory_decrypt(path, key, ciphertext, client: , context: nil) log_warning(DEV_WARNING) if self.in_memory_warnings_enabled? return nil if ciphertext.nil? raise Vault::Rails::InvalidCiphertext.new(ciphertext) if !ciphertext.start_with?(DEV_PREFIX) data = ciphertext[DEV_PREFIX.length..-1] cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.decrypt cipher.key = memory_key_for(path, key, context: context) return cipher.update(Base64.strict_decode64(data)) + cipher.final end
Perform in-memory encryption. This is useful for testing and development.
# File lib/vault/rails.rb, line 178 def memory_encrypt(path, key, plaintext, client: , context: nil) log_warning(DEV_WARNING) if self.in_memory_warnings_enabled? return nil if plaintext.nil? cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.encrypt cipher.key = memory_key_for(path, key, context: context) return DEV_PREFIX + Base64.strict_encode64(cipher.update(plaintext) + cipher.final) end
The symmetric key for the given params. @return [String]
# File lib/vault/rails.rb, line 206 def memory_key_for(path, key, context: nil) md5 = OpenSSL::Digest::MD5.new md5 << path md5 << key md5 << context if context md5.digest end
Perform decryption using Vault
. This will raise exceptions if Vault
is unavailable.
# File lib/vault/rails.rb, line 231 def vault_decrypt(path, key, ciphertext, client: , context: nil) return nil if ciphertext.nil? route = File.join(path, "decrypt", key) data = { ciphertext: ciphertext } data[:context] = Base64.strict_encode64(context) if context secret = client.logical.write(route, data) return Base64.strict_decode64(secret.data[:plaintext]) end
Perform encryption using Vault
. This will raise exceptions if Vault
is unavailable.
# File lib/vault/rails.rb, line 216 def vault_encrypt(path, key, plaintext, client: , context: nil) return nil if plaintext.nil? route = File.join(path, "encrypt", key) data = { plaintext: Base64.strict_encode64(plaintext) } data[:context] = Base64.strict_encode64(context) if context secret = client.logical.write(route, data) return secret.data[:ciphertext] end
Private Class Methods
# File lib/vault/rails.rb, line 272 def log_warning(msg) if defined?(::Rails) && ::Rails.logger != nil ::Rails.logger.warn { msg } end end
# File lib/vault/rails.rb, line 278 def transform_role_name(opts) opts[:role] || self.default_role_name || self.application end
# File lib/vault/rails.rb, line 254 def with_retries(client = self.client, &block) exceptions = [Vault::HTTPConnectionError, Vault::HTTPServerError, Vault::MissingRequiredStateError] options = { attempts: self.retry_attempts, base: self.retry_base, max_wait: self.retry_max_wait, } client.with_retries(*exceptions, options) do |i, e| if !e.nil? log_warning "[vault-rails] (#{i}) An error occurred when trying to " \ "communicate with Vault: #{e.message}" end yield end end