class ActiveSupport::EncryptedFile

Constants

CIPHER

Attributes

content_path[R]
env_key[R]
key_path[R]
raise_if_missing_key[R]

Public Class Methods

generate_key() click to toggle source
# File lib/active_support/encrypted_file.rb, line 31
def self.generate_key
  SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
end
new(content_path:, key_path:, env_key:, raise_if_missing_key:) click to toggle source
# File lib/active_support/encrypted_file.rb, line 42
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
  @content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
  @key_path = Pathname.new(key_path)
  @env_key, @raise_if_missing_key = env_key, raise_if_missing_key
end

Public Instance Methods

change(&block) click to toggle source
# File lib/active_support/encrypted_file.rb, line 77
def change(&block)
  writing read, &block
end
key() click to toggle source

Returns the encryption key, first trying the environment variable specified by env_key, then trying the key file specified by key_path. If raise_if_missing_key is true, raises MissingKeyError if the environment variable is not set and the key file does not exist.

# File lib/active_support/encrypted_file.rb, line 52
def key
  read_env_key || read_key_file || handle_missing_key
end
read() click to toggle source

Reads the file and returns the decrypted content.

Raises:

# File lib/active_support/encrypted_file.rb, line 64
def read
  if !key.nil? && content_path.exist?
    decrypt content_path.binread
  else
    raise MissingContentError, content_path
  end
end
write(contents) click to toggle source
# File lib/active_support/encrypted_file.rb, line 72
def write(contents)
  IO.binwrite "#{content_path}.tmp", encrypt(contents)
  FileUtils.mv "#{content_path}.tmp", content_path
end

Private Instance Methods

check_key_length() click to toggle source
# File lib/active_support/encrypted_file.rb, line 124
def check_key_length
  raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
end
decrypt(contents) click to toggle source
# File lib/active_support/encrypted_file.rb, line 102
def decrypt(contents)
  encryptor.decrypt_and_verify contents
end
encrypt(contents) click to toggle source
# File lib/active_support/encrypted_file.rb, line 97
def encrypt(contents)
  check_key_length
  encryptor.encrypt_and_sign contents
end
encryptor() click to toggle source
# File lib/active_support/encrypted_file.rb, line 106
def encryptor
  @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
end
handle_missing_key() click to toggle source
# File lib/active_support/encrypted_file.rb, line 120
def handle_missing_key
  raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
end
read_env_key() click to toggle source
# File lib/active_support/encrypted_file.rb, line 111
def read_env_key
  ENV[env_key].presence
end
read_key_file() click to toggle source
# File lib/active_support/encrypted_file.rb, line 115
def read_key_file
  return @key_file_contents if defined?(@key_file_contents)
  @key_file_contents = (key_path.binread.strip if key_path.exist?)
end
writing(contents) { |tmp_path| ... } click to toggle source
# File lib/active_support/encrypted_file.rb, line 83
def writing(contents)
  Tempfile.create(["", "-" + content_path.basename.to_s.chomp(".enc")]) do |tmp_file|
    tmp_path = Pathname.new(tmp_file)
    tmp_path.binwrite contents

    yield tmp_path

    updated_contents = tmp_path.binread

    write(updated_contents) if updated_contents != contents
  end
end