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.rb117
1 files changed, 87 insertions, 30 deletions
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index e346d5cc3a..038284ebdd 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -7,6 +7,7 @@ require "active_support/key_generator"
require "active_support/message_verifier"
require "active_support/encrypted_configuration"
require "active_support/deprecation"
+require "active_support/hash_with_indifferent_access"
require "rails/engine"
require "rails/secrets"
@@ -172,14 +173,9 @@ module Rails
def key_generator
# 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 secret_key_base
- ActiveSupport::CachingKeyGenerator.new(
- ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
- )
- else
- ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token)
- end
+ @caching_key_generator ||= ActiveSupport::CachingKeyGenerator.new(
+ ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
+ )
end
# Returns a message verifier object.
@@ -232,7 +228,12 @@ module Rails
if yaml.exist?
require "erb"
- (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
+ config = YAML.load(ERB.new(yaml.read).result) || {}
+ config = (config["shared"] || {}).merge(config[env] || {})
+
+ ActiveSupport::OrderedOptions.new.tap do |options|
+ options.update(NonSymbolAccessDeprecatedHash.new(config))
+ end
else
raise "Could not load configuration. No such file - #{yaml}"
end
@@ -249,7 +250,6 @@ module Rails
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" => secret_key_base,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
@@ -267,6 +267,7 @@ module Rails
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer,
"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
@@ -373,9 +374,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.
#
@@ -400,34 +399,25 @@ module Rails
# 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
- end
+ 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 development and test, this is randomly generated and stored in a
+ # temporary file in <tt>tmp/development_secret.txt</tt>.
#
# 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)
+ if Rails.env.development? || Rails.env.test?
+ secrets.secret_key_base ||= generate_development_secret
else
validate_secret_key_base(
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
@@ -438,13 +428,17 @@ module Rails
# 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+.
+ # If specific credentials file exists for current environment, it takes precedence, thus for +production+
+ # environment look first for +config/credentials/production.yml.enc+ with master key taken
+ # from <tt>ENV["RAILS_MASTER_KEY"]</tt> or from loading +config/credentials/production.key+.
+ # Default behavior can be overwritten by setting +config.credentials.content_path+ and +config.credentials.key_path+.
def credentials
- @credentials ||= encrypted("config/credentials.yml.enc")
+ @credentials ||= encrypted(config.credentials.content_path, key_path: config.credentials.key_path)
end
# Shorthand to decrypt any encrypted configurations or files.
#
- # For any file added with <tt>bin/rails encrypted:edit</tt> call +read+ to decrypt
+ # 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>.
#
@@ -581,13 +575,29 @@ module Rails
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?
+ else
raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `rails credentials:edit`"
end
end
private
+ def generate_development_secret
+ if secrets.secret_key_base.nil?
+ key_file = Rails.root.join("tmp/development_secret.txt")
+
+ if !File.exist?(key_file)
+ random_key = SecureRandom.hex(64)
+ FileUtils.mkdir_p(key_file.dirname)
+ File.binwrite(key_file, random_key)
+ end
+
+ secrets.secret_key_base = File.binread(key_file)
+ end
+
+ secrets.secret_key_base
+ end
+
def build_request(env)
req = super
env["ORIGINAL_FULLPATH"] = req.fullpath
@@ -598,5 +608,52 @@ module Rails
def build_middleware
config.app_middleware + super
end
+
+ class NonSymbolAccessDeprecatedHash < HashWithIndifferentAccess # :nodoc:
+ def initialize(value = nil)
+ if value.is_a?(Hash)
+ value.each_pair { |k, v| self[k] = v }
+ else
+ super
+ end
+ end
+
+ def []=(key, value)
+ regular_writer(key.to_sym, convert_value(value, for: :assignment))
+ end
+
+ private
+
+ def convert_key(key)
+ unless key.kind_of?(Symbol)
+ ActiveSupport::Deprecation.warn(<<~MESSAGE.squish)
+ Accessing hashes returned from config_for by non-symbol keys
+ is deprecated and will be removed in Rails 6.1.
+ Use symbols for access instead.
+ MESSAGE
+
+ key = key.to_sym
+ end
+
+ key
+ end
+
+ def convert_value(value, options = {}) # :doc:
+ if value.is_a? Hash
+ if options[:for] == :to_hash
+ value.to_hash
+ else
+ self.class.new(value)
+ end
+ elsif value.is_a?(Array)
+ if options[:for] != :assignment || value.frozen?
+ value = value.dup
+ end
+ value.map! { |e| convert_value(e, options) }
+ else
+ value
+ end
+ end
+ end
end
end