123456789_123456789_123456789_123456789_123456789_

Class: ActiveSupport::EncryptedFile

Relationships & Source Files
Namespace Children
Exceptions:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Inherits: Object
Defined in: activesupport/lib/active_support/encrypted_file.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(content_path:, key_path:, env_key:, raise_if_missing_key:) ⇒ EncryptedFile

[ GitHub ]

  
# File 'activesupport/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

Class Method Details

.expected_key_length

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 35

def self.expected_key_length # :nodoc:
  @expected_key_length ||= generate_key.length
end

.generate_key

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 31

def self.generate_key
  SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
end

Instance Attribute Details

#content_path (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 40

attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key

#env_key (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 40

attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key

#key (readonly)

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 EncryptedFile::MissingKeyError if the environment variable is not set and the key file does not exist.

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 52

def key
  read_env_key || read_key_file || handle_missing_key
end

#key?Boolean (readonly)

Returns truthy if #key is truthy. Returns falsy otherwise. Unlike #key, does not raise EncryptedFile::MissingKeyError when #raise_if_missing_key is true.

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 58

def key?
  read_env_key || read_key_file
end

#key_path (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 40

attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key

#raise_if_missing_key (readonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 40

attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key

Instance Method Details

#change(&block)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 83

def change(&block)
  writing read, &block
end

#check_key_length (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 129

def check_key_length
  raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
end

#decrypt(contents) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 108

def decrypt(contents)
  encryptor.decrypt_and_verify contents
end

#encrypt(contents) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 103

def encrypt(contents)
  check_key_length
  encryptor.encrypt_and_sign contents
end

#encryptor (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 112

def encryptor
  @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER, serializer: Marshal)
end

#handle_missing_key (private)

Raises:

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 125

def handle_missing_key
  raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
end

#read

Reads the file and returns the decrypted content.

Raises:

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 70

def read
  if !key.nil? && content_path.exist?
    decrypt content_path.binread.strip
  else
    raise MissingContentError, content_path
  end
end

#read_env_key (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 117

def read_env_key
  ENV[env_key].presence
end

#read_key_file (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 121

def read_key_file
  @key_file_contents ||= (key_path.binread.strip if key_path.exist?)
end

#write(contents)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 78

def write(contents)
  IO.binwrite "#{content_path}.tmp", encrypt(contents)
  FileUtils.mv "#{content_path}.tmp", content_path
end

#writing(contents) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/encrypted_file.rb', line 89

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