# EJSON::Rails [![Build Status](https://github.com/Shopify/ejson-rails/workflows/CI/badge.svg?branch=main)](https://github.com/Shopify/ejson-rails/actions?query=branch%3Amain) Automatically injects [`ejson`](https://github.com/Shopify/ejson) decrypted secrets into your `Rails.application.secrets`. ## Installation Add this line to your application's Gemfile: ```ruby gem 'ejson-rails' ``` And then execute: $ bundle Or install it yourself as: $ gem install ejson-rails ## Usage Decrypted secrets from `project/config/secrets.json` (or `project/config/secrets.{current_rails_environment}.json` if that doesn't exist) will be accessible via `Rails.application.secrets`. For example: ```json // project/config/secrets.json { "some_secret": "key" } ``` will be accessible via `Rails.application.secrets.some_secret` or `Rails.application.secrets[:some_secret]` on boot. JSON files are loaded once and contents are `deep_merge`'d into your app's existing rails secrets. Secrets will also be accessible via `Rails.application.credentials`, e.g. `Rails.application.credentials.some_secret` or `Rails.application.credentials[:some_secret]`. To avoid subtle compatibility issues, if a credential already exists, an error will occur. NOTE: This gem does not decrypt ejson for you. You will need to configure this as part of your deployment pipeline. ## Migrating to credentials Rails 7.1 has deprecated application secrets in favor of credentials. ejson-rails can migrate secrets to application credentials. Even before running Rails 7.1, you can migrate your secrets in a few steps. First, move the development and test secrets to JSON secrets: ```sh-session bin/rails runner 'Rails.root.join("config/secrets.#{Rails.env}.json").write(JSON.pretty_generate(Rails.application.secrets.to_h.without(:secret_key_base)))' bin/rails runner -e test 'Rails.root.join("config/secrets.#{Rails.env}.json").write(JSON.pretty_generate(Rails.application.secrets.to_h.without(:secret_key_base)))' ``` Secrets support ERB while EJSON secrets don't, so if your secrets contain ERB, you will need to move that logic to the environment configurations: **Before**: `config/secrets.yml` ```yaml development: some_external_service: api_token: <%= ENV.fetch(SOME_EXTERNAL_SERVICE_API_TOKEN, "12345") %> ``` **After**: `config/secrets.development.json` as generated by the command above. ```json { "some_external_service": { "api_token": "12345" }, "something_else_entirely": "abc" } ``` `config/environments/development.rb` ```ruby Rails.application.configure do # elided # credential should be set using []=, not the dynamic accessors credentials[:some_external_service][:api_token] = ENV.fetch("SOME_EXTERNAL_SERVICE_API_TOKEN", "12345") # top-level values must be set through `credentials.config` credentials.config[:something_else_entirely] = ENV.fetch("SOMETHING_ELSE_ENTIRELY", "abc") end ``` Note that the code accesses the credentials as a Hash with `[]` and `[]=`. This is important because the dynamic accessor methods will set values in a different object, and credentials will behave inconsistently after that: ```ruby Rails.application.credentials.some_external_service.api_token = "foo" Rails.application.credentials[:some_external_service][:api_token] # => "12345" ``` Also note the code sets top-level values through `credentials.config`, because `credentials#[]=(key, value)` sets values in a different object. ```ruby Rails.application.credentials[:something_else_entirely] = "foo" Rails.application.credentials[:something_else_entirely] # => "abc" ``` Make sure there's no code using the dynamic accessors before setting the configuration in the Hash, or the values won't be accessible from the dynamic accessor: ```ruby Rails.application.credentials.something_else_entirely # just accessing is enough to cause the issue Rails.application.credentials[:some_external_service][:api_token] = "foo" Rails.application.credentials.some_external_service.api_token # => "12345" ``` You're now ready to use credentials instead of secrets: ```sh-session git ls-files | xargs ruby -pi -e 'gsub("Rails.application.secrets", "Rails.application.credentials")' -- ``` To avoid the deprecation warning from the use of secrets in `ejson-rails` once you're running Rails 7.1, require another file from your Gemfile: ```ruby gem 'ejson-rails', require: 'ejson/rails/skip_secrets' ``` This will no longer merge secrets from JSON in `Rails.application.secrets`. This will be the default in the next major version. ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/ejson-rails. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).