diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 8d82421d46481..315ce75bc6e6f 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated `Rails.application.secrets`. + + *Rafael Mendonça França* + * Remove deprecated `ActiveSupport::Notifications::Event#children` and `ActiveSupport::Notifications::Event#parent_of?`. *Rafael Mendonça França* diff --git a/guides/source/7_2_release_notes.md b/guides/source/7_2_release_notes.md index 0d2e7df98f5ec..9b5754402fdc6 100644 --- a/guides/source/7_2_release_notes.md +++ b/guides/source/7_2_release_notes.md @@ -207,6 +207,8 @@ Please refer to the [Changelog][active-support] for detailed changes. ### Removals +* Remove deprecated `Rails.application.secrets`. + * Remove deprecated `ActiveSupport::Notifications::Event#children` and `ActiveSupport::Notifications::Event#parent_of?`. * Remove deprecated support to call the following methods without passing a deprecator: diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 353a14cd00ad4..bf20f1f29bfaa 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -10,7 +10,6 @@ require "active_support/hash_with_indifferent_access" require "active_support/configuration_file" require "rails/engine" -require "rails/secrets" require "rails/autoloaders" module Rails @@ -104,7 +103,7 @@ def find_root(from) delegate :default_url_options, :default_url_options=, to: :routes INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders, - :routes, :helpers, :app_env_config, :secrets] # :nodoc: + :routes, :helpers, :app_env_config] # :nodoc: def initialize(initial_variable_values = {}, &block) super() @@ -439,25 +438,7 @@ def config # :nodoc: end attr_writer :config - - def secrets - Rails.deprecator.warn(<<~MSG.squish) - `Rails.application.secrets` is deprecated in favor of `Rails.application.credentials` and will be removed in Rails 7.2. - MSG - @secrets ||= begin - secrets = ActiveSupport::OrderedOptions.new - files = config.paths["config/secrets"].existent - files = files.reject { |path| path.end_with?(".enc") } unless config.read_encrypted_secrets - secrets.merge! Rails::Secrets.parse(files, env: Rails.env) - - # Fallback to config.secret_key_base if secrets.secret_key_base isn't set - secrets.secret_key_base ||= config.secret_key_base - - secrets - end - end - - attr_writer :secrets, :credentials + attr_writer :credentials # The secret_key_base is used as the input secret to the application's key generator, which in turn # is used to create all ActiveSupport::MessageVerifier and ActiveSupport::MessageEncryptor instances, @@ -473,33 +454,16 @@ def secrets # Dockerfile example: RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile. # # In all other environments, we look for it first in ENV["SECRET_KEY_BASE"], - # then +credentials.secret_key_base+, and finally +secrets.secret_key_base+. For most applications, - # the correct place to store it is in the encrypted credentials file. + # then +credentials.secret_key_base+. For most applications, the correct place to store it is in the + # encrypted credentials file. def secret_key_base - config.secret_key_base ||= - if ENV["SECRET_KEY_BASE_DUMMY"] - generate_local_secret - else - validate_secret_key_base( - ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || begin - secret_skb = secrets_secret_key_base - - if secret_skb && secret_skb.equal?(config.secret_key_base) - config.secret_key_base - elsif secret_skb - Rails.deprecator.warn(<<~MSG.squish) - Your `secret_key_base` is configured in `Rails.application.secrets`, - which is deprecated in favor of `Rails.application.credentials` and - will be removed in Rails 7.2. - MSG - - secret_skb - elsif Rails.env.local? - generate_local_secret - end - end - ) - end + if Rails.env.local? || ENV["SECRET_KEY_BASE_DUMMY"] + config.secret_key_base ||= generate_local_secret + else + validate_secret_key_base( + ENV["SECRET_KEY_BASE"] || credentials.secret_key_base + ) + end end # Returns an ActiveSupport::EncryptedConfiguration instance for the @@ -674,8 +638,6 @@ def generate_local_secret if File.exist?(key_file) config.secret_key_base = File.binread(key_file) - elsif secrets_secret_key_base - config.secret_key_base = secrets_secret_key_base else random_key = SecureRandom.hex(64) FileUtils.mkdir_p(key_file.dirname) @@ -687,12 +649,6 @@ def generate_local_secret config.secret_key_base end - def secrets_secret_key_base - Rails.deprecator.silence do - secrets.secret_key_base - end - end - def build_request(env) req = super env["ORIGINAL_FULLPATH"] = req.fullpath diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index af488c459e353..95295522e7e85 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -5,7 +5,6 @@ require "active_support/notifications" require "active_support/dependencies" require "active_support/descendants_tracker" -require "rails/secrets" module Rails class Application @@ -114,10 +113,6 @@ module Bootstrap initializer :bootstrap_hook, group: :all do |app| ActiveSupport.run_load_hooks(:before_initialize, app) end - - initializer :set_secrets_root, group: :all do - Rails::Secrets.root = root - end end end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 2788305f68dbd..d688ed4530d2f 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -19,7 +19,7 @@ class Configuration < ::Rails::Engine::Configuration :ssl_options, :public_file_server, :session_options, :time_zone, :reload_classes_only_on_change, :beginning_of_week, :filter_redirect, :x, - :read_encrypted_secrets, :content_security_policy_report_only, + :content_security_policy_report_only, :content_security_policy_nonce_generator, :content_security_policy_nonce_directives, :require_master_key, :credentials, :disable_sandbox, :sandbox_by_default, :add_autoload_paths_to_load_path, :rake_eager_load, :server_timing, :log_file_size, @@ -68,7 +68,6 @@ def initialize(*) @debug_exception_response_format = nil @x = Custom.new @enable_dependency_loading = false - @read_encrypted_secrets = false @content_security_policy = nil @content_security_policy_report_only = false @content_security_policy_nonce_generator = nil @@ -373,6 +372,15 @@ def enable_dependency_loading=(value) @enable_dependency_loading = value end + def read_encrypted_secrets + Rails.deprecator.warn(`config.read_encrypted_secrets is deprecated and will be removed in Rails 7.3.`) + end + + def read_encrypted_secrets=(value) + Rails.deprecator.warn(`config.read_encrypted_secrets is deprecated and will be removed in Rails 7.3.`) + end + + def encoding=(value) @encoding = value silence_warnings do @@ -405,7 +413,6 @@ def paths @paths ||= begin paths = super paths.add "config/database", with: "config/database.yml" - paths.add "config/secrets", with: "config", glob: "secrets.yml{,.enc}" paths.add "config/environment", with: "config/environment.rb" paths.add "lib/templates" paths.add "log", with: "log/#{Rails.env}.log" diff --git a/railties/lib/rails/commands/secrets/USAGE b/railties/lib/rails/commands/secrets/USAGE deleted file mode 100644 index 55fbfb0a9a133..0000000000000 --- a/railties/lib/rails/commands/secrets/USAGE +++ /dev/null @@ -1,61 +0,0 @@ -Description: - ** DEPRECATED ** - Rails 5.2 has introduced a new `credentials` API that replaces Rails secrets. - Please use the Rails `credentials` commands instead. - Run `bin/rails credentials:help` for more information. - - The Rails `secrets` commands helps encrypting secrets to slim a production - environment's `ENV` hash. It's also useful for atomic deploys: no need to - coordinate key changes to get everything working as the keys are shipped - with the code. - -Setup: - Run `<%= executable(:setup) %>` to opt in and generate the `config/secrets.yml.key` - and `config/secrets.yml.enc` files. - - The latter contains all the keys to be encrypted while the former holds the - encryption key. - - Don't lose the key! Put it in a password manager your team can access. - Should you lose it no one, including you, will be able to access any encrypted - secrets. - Don't commit the key! Add `config/secrets.yml.key` to your source control's - ignore file. If you use Git, Rails handles this for you. - - Rails also looks for the key in `ENV["RAILS_MASTER_KEY"]` if that's easier to - manage. - - You could prepend that to your server's start command like this: - - RAILS_MASTER_KEY="im-the-master-now-hahaha" bin/rails server - - The `config/secrets.yml.enc` has much the same format as `config/secrets.yml`: - - production: - secret_key_base: so-secret-very-hidden-wow - payment_processing_gateway_key: much-safe-very-gaedwey-wow - - But that's where the similarities between `secrets.yml` and `secrets.yml.enc` - end, e.g. no keys from `secrets.yml` will be moved to `secrets.yml.enc` and - be encrypted. - - A `shared:` top level key is also supported such that any keys there is merged - into the other environments. - - Additionally, Rails won't read encrypted secrets out of the box even if you have - the key. Add this: - - config.read_encrypted_secrets = true - - to the environment you'd like to read encrypted secrets. `<%= executable(:setup) %>` - inserts this into the production environment by default. - -Editing Secrets: - After `<%= executable(:setup) %>`, run `<%= executable(:edit) %>`. - - That command opens a temporary file in `$VISUAL` or `$EDITOR` with the decrypted - contents of `config/secrets.yml.enc` to edit the encrypted secrets. - - When the temporary file is next saved the contents are encrypted and written to - `config/secrets.yml.enc` while the file itself is destroyed to prevent secrets - from leaking. diff --git a/railties/lib/rails/commands/secrets/secrets_command.rb b/railties/lib/rails/commands/secrets/secrets_command.rb deleted file mode 100644 index 09d864592bb32..0000000000000 --- a/railties/lib/rails/commands/secrets/secrets_command.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -require "active_support" -require "active_support/core_ext/string/filters" -require "rails/secrets" -require "rails/command/helpers/editor" - -module Rails - module Command - class SecretsCommand < Rails::Command::Base # :nodoc: - include Helpers::Editor - - desc "edit", "**deprecated** Open the secrets in `$VISUAL` or `$EDITOR` for editing" - def edit - Rails.deprecator.warn(<<~MSG.squish) - `bin/rails secrets:edit` is deprecated in favor of credentials and will be removed in Rails 7.2. - Run `bin/rails credentials:help` for more information. - MSG - - boot_application! - - using_system_editor do - Rails::Secrets.read_for_editing { |tmp_path| system_editor(tmp_path) } - say "File encrypted and saved." - end - rescue Rails::Secrets::MissingKeyError => error - say error.message - rescue Errno::ENOENT => error - if error.message.include?("secrets.yml.enc") - exit 1 - else - raise - end - end - - desc "show", "**deprecated** Show the decrypted secrets" - def show - Rails.deprecator.warn(<<~MSG.squish) - `bin/rails secrets:show` is deprecated in favor of credentials and will be removed in Rails 7.2. - Run `bin/rails credentials:help` for more information. - MSG - - say Rails::Secrets.read - end - end - end -end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 16dbddc39a939..7050bc00df622 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -203,7 +203,6 @@ def sorted_groups rails.map! { |n| n.delete_prefix("rails:") } rails.delete("app") rails.delete("plugin") - rails.delete("encrypted_secrets") rails.delete("encrypted_file") rails.delete("encryption_key_file") rails.delete("master_key") diff --git a/railties/lib/rails/secrets.rb b/railties/lib/rails/secrets.rb deleted file mode 100644 index 913d5e57c1bfb..0000000000000 --- a/railties/lib/rails/secrets.rb +++ /dev/null @@ -1,110 +0,0 @@ -# frozen_string_literal: true - -require "yaml" -require "tempfile" -require "active_support/message_encryptor" - -module Rails - # Greatly inspired by Ara T. Howard's magnificent sekrets gem. 😘 - class Secrets # :nodoc: - class MissingKeyError < RuntimeError - def initialize - super(<<-end_of_message.squish) - Missing encryption key to decrypt secrets with. - Ask your team for your master key and put it in ENV["RAILS_MASTER_KEY"] - end_of_message - end - end - - @cipher = "aes-128-gcm" - @root = File # Wonky, but ensures `join` uses the current directory. - - class << self - attr_writer :root - - def parse(paths, env:) - paths.each_with_object(Hash.new) do |path, all_secrets| - require "erb" - - source = ERB.new(preprocess(path)).result - secrets = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(source) : YAML.load(source) - secrets ||= {} - - all_secrets.merge!(secrets["shared"].deep_symbolize_keys) if secrets["shared"] - all_secrets.merge!(secrets[env].deep_symbolize_keys) if secrets[env] - end - end - - def key - ENV["RAILS_MASTER_KEY"] || read_key_file || handle_missing_key - end - - def encrypt(data) - encryptor.encrypt_and_sign(data) - end - - def decrypt(data) - encryptor.decrypt_and_verify(data) - end - - def read - decrypt(IO.binread(path)) - end - - def write(contents) - IO.binwrite("#{path}.tmp", encrypt(contents)) - FileUtils.mv("#{path}.tmp", path) - end - - def read_for_editing(&block) - writing(read, &block) - end - - private - def handle_missing_key - raise MissingKeyError - end - - def read_key_file - if File.exist?(key_path) - IO.binread(key_path).strip - end - end - - def key_path - @root.join("config", "secrets.yml.key") - end - - def path - @root.join("config", "secrets.yml.enc").to_s - end - - def preprocess(path) - if path.end_with?(".enc") - decrypt(IO.binread(path)) - else - IO.read(path) - end - end - - def writing(contents) - file_name = "#{File.basename(path)}.#{Process.pid}" - - Tempfile.create(["", "-" + file_name]) 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 - - def encryptor - @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: @cipher) - end - end - end -end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index ba6b67308aca5..f1abc4b70c88b 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -754,9 +754,7 @@ def index test "Use key_generator when secret_key_base is set" do make_basic_app do |application| - Rails.deprecator.silence do - application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" - end + application.config.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" application.config.session_store :disabled end @@ -776,9 +774,7 @@ def index test "application verifier can be used in the entire application" do make_basic_app do |application| - Rails.deprecator.silence do - application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" - end + application.config.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" application.config.session_store :disabled end @@ -921,114 +917,16 @@ def index assert_equal "old message", app.message_verifiers["salt"].verify(old_message) end - test "secrets is deprecated" do - app "development" - - assert_deprecated(Rails.deprecator) do - Rails.application.secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3" - end - end - - test "secrets.secret_key_base is used when config/secrets.yml is present" do - remove_file "config/credentials.yml.enc" - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - YAML - - app "development" - Rails.deprecator.silence do - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base - end - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secret_key_base - end test "secret_key_base is copied from config.secret_key_base when set" do - remove_file "config/secrets.yml" - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3" - RUBY - - app "development" - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secret_key_base - end - - test "config.secret_key_base does not lead to a deprecation" do - remove_file "config/secrets.yml" app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.credentials.secret_key_base = nil Rails.application.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c3" RUBY - app "production" - - assert_not_deprecated(Rails.deprecator) do - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secret_key_base - end - end - - test "config.secret_key_base leads to a deprecation in development when config/secrets.yml is present" do - remove_file "config/credentials.yml.enc" - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - YAML - app "development" - assert_deprecated(Rails.deprecator) do - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base - end assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secret_key_base end - test "custom secrets saved in config/secrets.yml are loaded in app secrets" do - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - aws_access_key_id: myamazonaccesskeyid - aws_secret_access_key: myamazonsecretaccesskey - YAML - - app "development" - - assert_equal "myamazonaccesskeyid", app.secrets.aws_access_key_id - assert_equal "myamazonsecretaccesskey", app.secrets.aws_secret_access_key - end - - test "shared secrets saved in config/secrets.yml are loaded in app secrets" do - app_file "config/secrets.yml", <<-YAML - shared: - api_key: 3b7cd727 - YAML - - app "development" - - assert_equal "3b7cd727", app.secrets.api_key - end - - test "shared secrets will yield to environment specific secrets" do - app_file "config/secrets.yml", <<-YAML - shared: - api_key: 3b7cd727 - - development: - api_key: abc12345 - YAML - - app "development" - - assert_equal "abc12345", app.secrets.api_key - end - - test "blank config/secrets.yml does not crash the loading process" do - app_file "config/secrets.yml", <<-YAML - YAML - - app "development" - - assert_nil app.secrets.not_defined - end - test "config.secret_key_base over-writes a blank app.secret_key_base" do app_file "config/initializers/secret_token.rb", <<-RUBY Rails.application.config.secret_key_base = "iaminallyoursecretkeybase" @@ -1039,20 +937,6 @@ def index assert_equal "iaminallyoursecretkeybase", app.secret_key_base end - test "that nested keys are symbolized the same as parents for hashes more than one level deep" do - app_file "config/secrets.yml", <<-YAML - development: - smtp_settings: - address: "smtp.example.com" - user_name: "postmaster@example.com" - password: "697361616320736c6f616e2028656c6f7265737429" - YAML - - app "development" - - assert_equal "697361616320736c6f616e2028656c6f7265737429", app.secrets.smtp_settings[:password] - end - test "require_master_key aborts app boot when missing key" do skip "can't run without fork" unless Process.respond_to?(:fork) diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb deleted file mode 100644 index 5edf24a18d467..0000000000000 --- a/railties/test/commands/secrets_test.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require "isolation/abstract_unit" -require "env_helpers" -require "rails/command" - -class Rails::Command::SecretsTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation, EnvHelpers - - setup :build_app - teardown :teardown_app - - test "edit without visual or editor gives hint" do - assert_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "", editor: "") - end - - test "edit with visual but not editor does not give hint" do - assert_no_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "cat", editor: "") - end - - test "edit with editor but not visual does not give hint" do - assert_no_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "", editor: "cat") - end - - test "edit secrets" do - # Use expected default MessageEncryptor serializer for Rails < 7.1 to be compatible with hardcoded secrets.yml.enc - add_to_config <<-RUBY - config.active_support.message_serializer = :marshal - RUBY - - require "#{app_path}/config/environment" - - prevent_deprecation - - # Run twice to ensure encrypted secrets can be reread after first edit pass. - 2.times do - assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_edit_command) - end - end - - test "show secrets" do - prevent_deprecation - - assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_show_command) - end - - private - def prevent_deprecation - Dir.chdir(app_path) do - File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") - File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") - end - end - - def run_edit_command(visual: "cat", editor: "cat") - switch_env("VISUAL", visual) do - switch_env("EDITOR", editor) do - rails "secrets:edit", allow_failure: true - end - end - end - - def run_show_command - rails "secrets:show", allow_failure: true - end -end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 6990d0acdc5aa..c5a24d4ac2c17 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -34,7 +34,6 @@ require "active_support/testing/isolation" require "active_support/core_ext/kernel/reporting" require "tmpdir" -require "rails/secrets" module TestHelpers module Paths diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb deleted file mode 100644 index 133d851819e72..0000000000000 --- a/railties/test/secrets_test.rb +++ /dev/null @@ -1,176 +0,0 @@ -# frozen_string_literal: true - -require "isolation/abstract_unit" -require "rails/secrets" - -class Rails::SecretsTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation - - setup :build_app - teardown :teardown_app - - test "setting read to false skips parsing" do - run_secrets_generator do - Rails::Secrets.write(<<-end_of_secrets) - production: - yeah_yeah: lets-walk-in-the-cool-evening-light - end_of_secrets - - add_to_env_config("production", "config.read_encrypted_secrets = false") - app("production") - - assert_not Rails.application.secrets.yeah_yeah - end - end - - test "raises when reading secrets without a key" do - run_secrets_generator do - FileUtils.rm("config/secrets.yml.key") - - assert_raises Rails::Secrets::MissingKeyError do - Rails::Secrets.key - end - end - end - - test "reading with ENV variable" do - run_secrets_generator do - old_key = ENV["RAILS_MASTER_KEY"] - ENV["RAILS_MASTER_KEY"] = IO.binread("config/secrets.yml.key").strip - FileUtils.rm("config/secrets.yml.key") - - assert_match "# production:\n# external_api_key:", Rails::Secrets.read - ensure - ENV["RAILS_MASTER_KEY"] = old_key - end - end - - test "reading from key file" do - run_secrets_generator do - File.binwrite("config/secrets.yml.key", "00112233445566778899aabbccddeeff") - - assert_equal "00112233445566778899aabbccddeeff", Rails::Secrets.key - end - end - - test "editing" do - run_secrets_generator do - decrypted_path = nil - - Rails::Secrets.read_for_editing do |tmp_path| - decrypted_path = tmp_path - - assert_match(/# production:\n# external_api_key/, File.read(tmp_path)) - - File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") - end - - assert_not File.exist?(decrypted_path) - assert_equal "Empty streets, empty nights. The Downtown Lights.", Rails::Secrets.read - end - end - - test "merging secrets with encrypted precedence" do - run_secrets_generator do - File.write("config/secrets.yml", <<-end_of_secrets) - production: - yeah_yeah: lets-go-walking-down-this-empty-street - end_of_secrets - - Rails::Secrets.write(<<-end_of_secrets) - production: - yeah_yeah: lets-walk-in-the-cool-evening-light - end_of_secrets - - add_to_env_config("production", "config.read_encrypted_secrets = true") - app("production") - - assert_equal "lets-walk-in-the-cool-evening-light", Rails.application.secrets.yeah_yeah - end - end - - test "refer secrets inside env config" do - run_secrets_generator do - Rails::Secrets.write(<<-end_of_yaml) - production: - some_secret: yeah yeah - end_of_yaml - - add_to_env_config "production", <<-end_of_config - config.dereferenced_secret = Rails.application.secrets.some_secret - end_of_config - - app("production") - - assert_equal "yeah yeah", Rails.application.config.dereferenced_secret - end - end - - test "do not update secrets.yml.enc when secretes do not change" do - run_secrets_generator do - Rails::Secrets.read_for_editing do |tmp_path| - File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") - end - - FileUtils.cp("config/secrets.yml.enc", "config/secrets.yml.enc.bk") - - Rails::Secrets.read_for_editing do |tmp_path| - File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") - end - - assert_equal File.read("config/secrets.yml.enc.bk"), File.read("config/secrets.yml.enc") - end - end - - test "can read secrets written in binary" do - run_secrets_generator do - secrets = <<-end_of_secrets - production: - api_key: 00112233445566778899aabbccddeeff… - end_of_secrets - - Rails::Secrets.write(secrets.dup.force_encoding(Encoding::ASCII_8BIT)) - - Rails::Secrets.read_for_editing do |tmp_path| - assert_match(/production:\n\s*api_key: 00112233445566778899aabbccddeeff…\n/, File.read(tmp_path)) - end - - app("production") - - assert_equal "00112233445566778899aabbccddeeff…", Rails.application.secrets.api_key - end - end - - test "can read secrets written in non-binary" do - run_secrets_generator do - secrets = <<-end_of_secrets - production: - api_key: 00112233445566778899aabbccddeeff… - end_of_secrets - - Rails::Secrets.write(secrets) - - Rails::Secrets.read_for_editing do |tmp_path| - assert_equal(secrets.dup.force_encoding(Encoding::ASCII_8BIT), IO.binread(tmp_path)) - end - - app("production") - - assert_equal "00112233445566778899aabbccddeeff…", Rails.application.secrets.api_key - end - end - - private - def run_secrets_generator - Dir.chdir(app_path) do - File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") - File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") - - add_to_config <<-RUBY - config.read_encrypted_secrets = true - RUBY - - yield - end - end -end