Encrypted Credentials in Rails 5.2

Episode #123 by Teacher's Avatar David Kimura

Summary

In this episode, we take a look at the Encrypted Credentials of Ruby on Rails 5.2 and how we can patch it so that we can use other YAML files like a development.yml and test.yml.
rails environment encryption 12:01

Resources

Source - https://github.com/driftingruby/123-encrypted-secrets-in-rails-52

Additional Notes: To make this more complete, looking at the config_for source, you could add in ERB support and parsing error handling.

# config_for
# File railties/lib/rails/application.rb, line 227
    def config_for(name)
      yaml = Pathname.new("#{paths["config"].existent.first}/#{name}.yml")

      if yaml.exist?
        require "erb"
        (YAML.load(ERB.new(yaml.read).result) || {})[Rails.env] || {}
      else
        raise "Could not load configuration. No such file - #{yaml}"
      end
    rescue Psych::SyntaxError => e
      raise "YAML syntax error occurred while parsing #{yaml}. " "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " "Error: #{e.message}"
    end

Download Source Code

Summary

# Terminal
rails credentials:help
rails credentials:edit

EDITOR='code --wait' rails credentials:edit

rails c
RAILS_ENV=production rails c

# Rails Console
Rails.application.credentials.aws
Rails.application.credentials.env.aws
Rails.application.credentials.env.aws[:access_key_id]
Rails.application.credentials.env.secret_key_base

# config/application.rb
require_relative 'boot'
require 'rails/all'
require_relative 'rails_env'
Bundler.require(*Rails.groups)

module Template
  class Application < Rails::Application
    config.load_defaults 5.2
    config.after_initialize do
      Rails.application.credentials.env = RailsEnv.new
    end
  end
end

# config/rails_env.rb
class RailsEnv
  def initialize
    load_environment_variables unless Rails.env.production?
    allow_encrypted_credentials
  end

  private

  def load_environment_variables
    return unless File.exist?(file_name)
    HashWithIndifferentAccess.new(YAML.safe_load(File.open(file_name))).each do |key, value|
      self.class.send :define_method, key.downcase do
        value
      end
    end
  end

  def allow_encrypted_credentials
    self.class.send :define_method, :method_missing do |m, *_args, &_block|
      Rails.application.credentials.send(m)
    end
  end

  def file_name
    File.join(Rails.root, 'config', "#{Rails.env}.yml")
  end
end