somleng/somleng-adhearsion

View on GitHub
components/services/lib/encrypted_credentials.rb

Summary

Maintainability
B
5 hrs
Test Coverage
require "yaml"
require "tempfile"
require "openssl"
require "base64"

module EncryptedCredentials
  class EncryptedFile
    CIPHER = "aes-256-cbc".freeze
    DEFAULT_KEY_PATH = Pathname(File.expand_path("../config/master.key", __dir__))
    DEFAULT_FILE_PATH = Pathname(File.expand_path("../config/credentials.yml.enc", __dir__))

    attr_reader :file_path, :key_path

    def initialize(file_path: DEFAULT_FILE_PATH, key_path: DEFAULT_KEY_PATH)
      @file_path = file_path
      @key_path = key_path
    end

    def credentials
      content = read_credentials
      YAML.load(content, aliases: true)
    end

    def edit
      Tempfile.open([ file_path.basename.to_s.chomp(".enc"), ".yml" ]) do |tmp_file|
        if file_path.exist?
          tmp_file.write(read_credentials)
          tmp_file.flush
          tmp_file.rewind
        end

        system("#{ENV['EDITOR']} #{tmp_file.path}")

        updated_contents = tmp_file.read
        encrypted_content = encrypt(updated_contents)

        IO.binwrite("#{file_path}.tmp", Base64.strict_encode64(encrypted_content))
        FileUtils.mv("#{file_path}.tmp", file_path.to_path)
      end
    end

    private

    def read_credentials
      encrypted_content = Base64.strict_decode64(file_path.binread)
      decrypt(encrypted_content)
    end

    def key
      read_env_key || read_key_file || handle_missing_key
    end

    def read_env_key
      ENV["APP_MASTER_KEY"]
    end

    def read_key_file
      key_path.binread.strip if key_path.exist?
    end

    def handle_missing_key
      raise "Missing key"
    end

    def encrypt(content)
      cipher = OpenSSL::Cipher.new(CIPHER).encrypt
      cipher.key = [ key ].pack("H*")
      cipher.update(content) + cipher.final
    end

    def decrypt(content)
      cipher = OpenSSL::Cipher.new(CIPHER).decrypt
      cipher.key = [ key ].pack("H*")
      cipher.update(content) + cipher.final
    end
  end
end