class Diffcrypt::Rails::EncryptedConfiguration

Attributes

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

Public Class Methods

new(config_path:, key_path:, env_key:, raise_if_missing_key:) click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 25
def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
  @content_path = Pathname.new(::File.absolute_path(config_path)).yield_self do |path|
    path.symlink? ? path.realpath : path
  end
  @diffcrypt_file = Diffcrypt::File.new(@content_path)
  @key_path = Pathname.new(key_path)
  @env_key = env_key
  @raise_if_missing_key = raise_if_missing_key
end

Public Instance Methods

change(&block) click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 69
def change(&block)
  writing read, &block
end
config() click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 59
def config
  @config ||= deserialize(read).deep_symbolize_keys
end
content_path_diffable?() click to toggle source

Determines if file is using the diffable format, or still encrypted using default rails credentials format @return [Boolean]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 38
def content_path_diffable?
  content_path.binread.index('---')&.zero?
end
key() click to toggle source

@raise [MissingKeyError] Will raise if key is not set @return [String]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 65
def key
  read_env_key || read_key_file || handle_missing_key
end
read() click to toggle source

Allow a config to be started without a file present @return [String] Returns decryped content or a blank string

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 44
def read
  raise MissingContentError, content_path unless !key.nil? && content_path.exist?

  decrypt content_path.binread
rescue MissingContentError
  ''
end
write(contents, original_encrypted_contents = nil) click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 52
def write(contents, original_encrypted_contents = nil)
  deserialize(contents)

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

Protected Instance Methods

active_support_encryptor() click to toggle source

Rails applications with an existing credentials file, the inbuilt active support encryptor should be used @return [ActiveSupport::MessageEncryptor]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 117
def active_support_encryptor
  @active_support_encryptor ||= ActiveSupport::MessageEncryptor.new(
    [key].pack('H*'),
    cipher: 'aes-128-gcm',
  )
end
decrypt(contents) click to toggle source

@param [String] contents @return [String]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 107
def decrypt(contents)
  if rails_native_credentials?(contents)
    active_support_encryptor.decrypt_and_verify contents
  else
    encryptor.decrypt contents
  end
end
deserialize(config) click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 147
def deserialize(config)
  YAML.safe_load(config).presence || {}
end
encrypt(contents, original_encrypted_contents = nil) click to toggle source

@param [String] contents The new content to be encrypted @param [String] diff_against The original (encrypted) content to determine which keys have changed @return [String] Encrypted content to commit

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 101
def encrypt(contents, original_encrypted_contents = nil)
  encryptor.encrypt contents, original_encrypted_contents
end
encryptor() click to toggle source

@return [Encryptor]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 125
def encryptor
  @encryptor ||= Encryptor.new key, cipher: @diffcrypt_file.cipher
end
handle_missing_key() click to toggle source

@raise [MissingKeyError] @return [void]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 139
def handle_missing_key
  raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
end
options() click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 143
def options
  @options ||= ActiveSupport::InheritableOptions.new(config)
end
rails_native_credentials?(contents) click to toggle source

Standard rails credentials encrypt the entire file. We need to detect this to use the correct data interface @return [Boolean]

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 94
def rails_native_credentials?(contents)
  contents.index('---').nil?
end
read_env_key() click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 129
def read_env_key
  ENV[env_key]
end
read_key_file() click to toggle source
# File lib/diffcrypt/rails/encrypted_configuration.rb, line 133
def read_key_file
  key_path.binread.strip if key_path.exist?
end
writing(contents) { |tmp_path| ... } click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/diffcrypt/rails/encrypted_configuration.rb, line 76
def writing(contents)
  tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
  tmp_path = Pathname.new ::File.join(Dir.tmpdir, tmp_file)
  tmp_path.binwrite contents

  yield tmp_path

  updated_contents = tmp_path.binread

  write(updated_contents, content_path_diffable? && content_path.binread)
ensure
  ::FileUtils.rm(tmp_path) if tmp_path&.exist?
end