lib/rails/application.rb in railties-7.0.8.6 vs lib/rails/application.rb in railties-7.1.0.beta1
- old
+ new
@@ -2,11 +2,12 @@
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/message_verifiers"
+require "active_support/deprecation"
require "active_support/encrypted_configuration"
require "active_support/hash_with_indifferent_access"
require "active_support/configuration_file"
require "rails/engine"
require "rails/secrets"
@@ -24,11 +25,11 @@
#
# == \Configuration
#
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
# the application object has several specific configurations, for example
- # +cache_classes+, +consider_all_requests_local+, +filter_parameters+,
+ # +enable_reloading+, +consider_all_requests_local+, +filter_parameters+,
# +logger+, and so forth.
#
# Check Rails::Application::Configuration to see them all.
#
# == Routes
@@ -68,10 +69,12 @@
class << self
def inherited(base)
super
Rails.app_class = base
+ # lib has to be added to $LOAD_PATH unconditionally, even if it's in the
+ # autoload paths and config.add_autoload_paths_to_load_path is false.
add_lib_to_load_path!(find_root(base.called_from))
ActiveSupport.run_load_hooks(:before_configuration, base)
end
def instance
@@ -109,11 +112,13 @@
@reloaders = []
@routes_reloader = nil
@app_env_config = nil
@ordered_railties = nil
@railties = nil
- @message_verifiers = {}
+ @key_generators = {}
+ @message_verifiers = nil
+ @deprecators = nil
@ran_load_hooks = false
@executor = Class.new(ActiveSupport::Executor)
@reloader = Class.new(ActiveSupport::Reloader)
@reloader.executor = @executor
@@ -147,19 +152,57 @@
# Reload application routes regardless if they changed or not.
def reload_routes!
routes_reloader.reload!
end
- # Returns the application's KeyGenerator
- def key_generator
+ # Returns a key generator (ActiveSupport::CachingKeyGenerator) for a
+ # specified +secret_key_base+. The return value is memoized, so additional
+ # calls with the same +secret_key_base+ will return the same key generator
+ # instance.
+ def key_generator(secret_key_base = self.secret_key_base)
# 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 ||= ActiveSupport::CachingKeyGenerator.new(
+ @key_generators[secret_key_base] ||= ActiveSupport::CachingKeyGenerator.new(
ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
)
end
+ # Returns a message verifier factory (ActiveSupport::MessageVerifiers). This
+ # factory can be used as a central point to configure and create message
+ # verifiers (ActiveSupport::MessageVerifier) for your application.
+ #
+ # By default, message verifiers created by this factory will generate
+ # messages using the default ActiveSupport::MessageVerifier options. You can
+ # override these options with a combination of
+ # ActiveSupport::MessageVerifiers#clear_rotations and
+ # ActiveSupport::MessageVerifiers#rotate. However, this must be done prior
+ # to building any message verifier instances. For example, in a
+ # +before_initialize+ block:
+ #
+ # # Use `url_safe: true` when generating messages
+ # config.before_initialize do |app|
+ # app.message_verifiers.clear_rotations
+ # app.message_verifiers.rotate(url_safe: true)
+ # end
+ #
+ # Message verifiers created by this factory will always use a secret derived
+ # from #secret_key_base when generating messages. +clear_rotations+ will not
+ # affect this behavior. However, older +secret_key_base+ values can be
+ # rotated for verifying messages:
+ #
+ # # Fall back to old `secret_key_base` when verifying messages
+ # config.before_initialize do |app|
+ # app.message_verifiers.rotate(secret_key_base: "old secret_key_base")
+ # end
+ #
+ def message_verifiers
+ @message_verifiers ||=
+ ActiveSupport::MessageVerifiers.new do |salt, secret_key_base: self.secret_key_base|
+ key_generator(secret_key_base).generate_key(salt)
+ end.rotate_defaults
+ end
+
# Returns a message verifier object.
#
# This verifier can be used to generate and verify signed messages in the application.
#
# It is recommended not to use the same verifier for different things, so you can get different
@@ -175,17 +218,24 @@
# Rails.application.message_verifier('sensitive_data').verify(message)
# # => 'my sensible data'
#
# See the ActiveSupport::MessageVerifier documentation for more information.
def message_verifier(verifier_name)
- @message_verifiers[verifier_name] ||= begin
- secret = key_generator.generate_key(verifier_name.to_s)
- ActiveSupport::MessageVerifier.new(secret)
+ message_verifiers[verifier_name]
+ end
+
+ # A managed collection of deprecators (ActiveSupport::Deprecation::Deprecators).
+ # The collection's configuration methods affect all deprecators in the
+ # collection. Additionally, the collection's +silence+ method silences all
+ # deprecators in the collection for the duration of a given block.
+ def deprecators
+ @deprecators ||= ActiveSupport::Deprecation::Deprecators.new.tap do |deprecators|
+ deprecators[:railties] = Rails.deprecator
end
end
- # Convenience for loading config/foo.yml for the current Rails env.
+ # Convenience for loading config/foo.yml for the current \Rails env.
#
# Examples:
#
# # config/exception_notification.yml:
# production:
@@ -243,20 +293,21 @@
else
raise "Could not load configuration. No such file - #{yaml}"
end
end
- # Stores some of the Rails initial environment parameters which
+ # Stores some of the \Rails initial environment parameters which
# will be used by middlewares and engines to configure themselves.
def env_config
@app_env_config ||= super.merge(
- "action_dispatch.parameter_filter" => config.filter_parameters,
+ "action_dispatch.parameter_filter" => filter_parameters,
"action_dispatch.redirect_filter" => config.filter_redirect,
"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.log_rescued_responses" => config.action_dispatch.log_rescued_responses,
+ "action_dispatch.debug_exception_log_level" => ActiveSupport::Logger.const_get(config.action_dispatch.debug_exception_log_level.to_s.upcase),
"action_dispatch.logger" => Rails.logger,
"action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
"action_dispatch.key_generator" => key_generator,
"action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt,
"action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
@@ -335,11 +386,11 @@
# Notice this method takes into consideration the default root path. So if you
# are changing config.root inside your application definition or having a custom
# Rails application, you will need to add lib to $LOAD_PATH on your own in case
# you need to load files in lib/ during the application configuration as well.
def self.add_lib_to_load_path!(root) # :nodoc:
- path = File.join root, "lib"
+ path = File.join(root, "lib")
if File.exist?(path) && !$LOAD_PATH.include?(path)
$LOAD_PATH.unshift(path)
end
end
@@ -385,10 +436,13 @@
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)
@@ -405,18 +459,24 @@
# 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,
# including the ones that sign and encrypt cookies.
#
# In development and test, this is randomly generated and stored in a
- # temporary file in <tt>tmp/development_secret.txt</tt>.
+ # temporary file in <tt>tmp/local_secret.txt</tt>.
#
+ # You can also set <tt>ENV["SECRET_KEY_BASE_DUMMY"]</tt> to trigger the use of a randomly generated
+ # secret_key_base that's stored in a temporary file. This is useful when precompiling assets for
+ # production as part of a build step that otherwise does not need access to the production secrets.
+ #
+ # Dockerfile example: <tt>RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile</tt>.
+ #
# In all other environments, we look for it first in <tt>ENV["SECRET_KEY_BASE"]</tt>,
# 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.development? || Rails.env.test?
- secrets.secret_key_base ||= generate_development_secret
+ 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 || secrets.secret_key_base
)
end
@@ -486,10 +546,15 @@
# +railties_order+.
def migration_railties # :nodoc:
ordered_railties.flatten - [self]
end
+ def load_generators(app = self) # :nodoc:
+ app.ensure_generator_templates_added
+ super
+ end
+
# Eager loads the application code.
def eager_load!
Rails.autoloaders.each(&:eager_load)
end
@@ -575,27 +640,41 @@
else
raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
end
end
+ def ensure_generator_templates_added
+ configured_paths = config.generators.templates
+ configured_paths.unshift(*(paths["lib/templates"].existent - configured_paths))
+ end
+
private
- def generate_development_secret
- if secrets.secret_key_base.nil?
- key_file = Rails.root.join("tmp/development_secret.txt")
+ def generate_local_secret
+ if config.secret_key_base.nil?
+ key_file = Rails.root.join("tmp/local_secret.txt")
- if !File.exist?(key_file)
+ 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)
File.binwrite(key_file, random_key)
+ config.secret_key_base = File.binread(key_file)
end
-
- secrets.secret_key_base = File.binread(key_file)
end
- secrets.secret_key_base
+ 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
env["ORIGINAL_SCRIPT_NAME"] = req.script_name
req
@@ -605,8 +684,17 @@
config.app_middleware + super
end
def coerce_same_site_protection(protection)
protection.respond_to?(:call) ? protection : proc { protection }
+ end
+
+ def filter_parameters
+ if config.precompile_filter_parameters
+ config.filter_parameters.replace(
+ ActiveSupport::ParameterFilter.precompile_filters(config.filter_parameters)
+ )
+ end
+ config.filter_parameters
end
end
end