aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/application.rb
diff options
context:
space:
mode:
Diffstat (limited to 'railties/lib/rails/application.rb')
-rw-r--r--railties/lib/rails/application.rb126
1 files changed, 101 insertions, 25 deletions
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 89f7b5991f..26ed195dcc 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
require "yaml"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/object/blank"
require "active_support/key_generator"
require "active_support/message_verifier"
+require "active_support/encrypted_configuration"
+require "active_support/deprecation"
require "rails/engine"
require "rails/secrets"
@@ -169,12 +173,10 @@ module Rails
# number of iterations selected based on consultation with the google security
# team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
@caching_key_generator ||=
- if secrets.secret_key_base
- unless secrets.secret_key_base.kind_of?(String)
- raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String, change this value in `config/secrets.yml`"
- end
- key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000)
- ActiveSupport::CachingKeyGenerator.new(key_generator)
+ if secret_key_base
+ ActiveSupport::CachingKeyGenerator.new(
+ ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
+ )
else
ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token)
end
@@ -230,7 +232,10 @@ module Rails
if yaml.exist?
require "erb"
- (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
+ require "active_support/ordered_options"
+
+ config = (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
+ ActiveSupport::InheritableOptions.new(config.deep_symbolize_keys)
else
raise "Could not load configuration. No such file - #{yaml}"
end
@@ -244,13 +249,11 @@ module Rails
# will be used by middlewares and engines to configure themselves.
def env_config
@app_env_config ||= begin
- validate_secret_key_config!
-
super.merge(
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.redirect_filter" => config.filter_redirect,
"action_dispatch.secret_token" => secrets.secret_token,
- "action_dispatch.secret_key_base" => secrets.secret_key_base,
+ "action_dispatch.secret_key_base" => secret_key_base,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
"action_dispatch.logger" => Rails.logger,
@@ -260,8 +263,17 @@ module Rails
"action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
"action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
"action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt,
+ "action_dispatch.authenticated_encrypted_cookie_salt" => config.action_dispatch.authenticated_encrypted_cookie_salt,
+ "action_dispatch.use_authenticated_cookie_encryption" => config.action_dispatch.use_authenticated_cookie_encryption,
+ "action_dispatch.encrypted_cookie_cipher" => config.action_dispatch.encrypted_cookie_cipher,
+ "action_dispatch.signed_cookie_digest" => config.action_dispatch.signed_cookie_digest,
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
- "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest
+ "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest,
+ "action_dispatch.cookies_rotations" => config.action_dispatch.cookies_rotations,
+ "action_dispatch.use_cookies_with_metadata" => config.action_dispatch.use_cookies_with_metadata,
+ "action_dispatch.content_security_policy" => config.content_security_policy,
+ "action_dispatch.content_security_policy_report_only" => config.content_security_policy_report_only,
+ "action_dispatch.content_security_policy_nonce_generator" => config.content_security_policy_nonce_generator
)
end
end
@@ -365,9 +377,7 @@ module Rails
@config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
end
- def config=(configuration) #:nodoc:
- @config = configuration
- end
+ attr_writer :config
# Returns secrets added to config/secrets.yml.
#
@@ -386,19 +396,86 @@ module Rails
def secrets
@secrets ||= begin
secrets = ActiveSupport::OrderedOptions.new
- secrets.merge! Rails::Secrets.parse(config.paths["config/secrets"].existent, env: Rails.env)
+ 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
# Fallback to config.secret_token if secrets.secret_token isn't set
secrets.secret_token ||= config.secret_token
+ if secrets.secret_token.present?
+ ActiveSupport::Deprecation.warn(
+ "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0."
+ )
+ end
+
secrets
end
end
- def secrets=(secrets) #:nodoc:
- @secrets = secrets
+ attr_writer :secrets
+
+ # The secret_key_base is used as the input secret to the application's key generator, which in turn
+ # is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
+ #
+ # In test and development, this is simply derived as a MD5 hash of the application's name.
+ #
+ # 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.
+ def secret_key_base
+ if Rails.env.test? || Rails.env.development?
+ secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
+ else
+ validate_secret_key_base(
+ ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
+ )
+ end
+ end
+
+ # Decrypts the credentials hash as kept in +config/credentials.yml.enc+. This file is encrypted with
+ # the Rails master key, which is either taken from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading
+ # +config/master.key+.
+ def credentials
+ @credentials ||= encrypted("config/credentials.yml.enc")
+ end
+
+ # Shorthand to decrypt any encrypted configurations or files.
+ #
+ # For any file added with <tt>rails encrypted:edit</tt> call +read+ to decrypt
+ # the file with the master key.
+ # The master key is either stored in +config/master.key+ or <tt>ENV["RAILS_MASTER_KEY"]</tt>.
+ #
+ # Rails.application.encrypted("config/mystery_man.txt.enc").read
+ # # => "We've met before, haven't we?"
+ #
+ # It's also possible to interpret encrypted YAML files with +config+.
+ #
+ # Rails.application.encrypted("config/credentials.yml.enc").config
+ # # => { next_guys_line: "I don't think so. Where was it you think we met?" }
+ #
+ # Any top-level configs are also accessible directly on the return value:
+ #
+ # Rails.application.encrypted("config/credentials.yml.enc").next_guys_line
+ # # => "I don't think so. Where was it you think we met?"
+ #
+ # The files or configs can also be encrypted with a custom key. To decrypt with
+ # a key in the +ENV+, use:
+ #
+ # Rails.application.encrypted("config/special_tokens.yml.enc", env_key: "SPECIAL_TOKENS")
+ #
+ # Or to decrypt with a file, that should be version control ignored, relative to +Rails.root+:
+ #
+ # Rails.application.encrypted("config/special_tokens.yml.enc", key_path: "config/special_tokens.key")
+ def encrypted(path, key_path: "config/master.key", env_key: "RAILS_MASTER_KEY")
+ ActiveSupport::EncryptedConfiguration.new(
+ config_path: Rails.root.join(path),
+ key_path: Rails.root.join(key_path),
+ env_key: env_key,
+ raise_if_missing_key: config.require_master_key
+ )
end
def to_app #:nodoc:
@@ -499,14 +576,13 @@ module Rails
default_stack.build_stack
end
- def validate_secret_key_config! #:nodoc:
- if secrets.secret_key_base.blank?
- ActiveSupport::Deprecation.warn "You didn't set `secret_key_base`. " \
- "Read the upgrade documentation to learn more about this new config option."
-
- if secrets.secret_token.blank?
- raise "Missing `secret_key_base` for '#{Rails.env}' environment, set this value in `config/secrets.yml`"
- end
+ def validate_secret_key_base(secret_key_base)
+ if secret_key_base.is_a?(String) && secret_key_base.present?
+ secret_key_base
+ elsif secret_key_base
+ raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String`"
+ elsif secrets.secret_token.blank?
+ raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `rails credentials:edit`"
end
end