diff --git a/Gemfile b/Gemfile index d516abe111..dd26a9d1c2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,7 @@ -path File.dirname(__FILE__) source 'http://rubygems.org' gem "arel", :git => "git://github.com/rails/arel.git" -gem "rails", "3.0.0.beta2" +gem "rails", :path => File.dirname(__FILE__) gem "rake", ">= 0.8.7" gem "mocha", ">= 0.9.8" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index d2118ec483..1dfc240029 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -2,7 +2,7 @@ module ActionController class Base < Metal abstract! - def self.modules_without(*modules) + def self.without_modules(*modules) modules = modules.map do |m| m.is_a?(Symbol) ? ActionController.const_get(m) : m end diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb index 51d1e23753..05551ffee4 100644 --- a/actionpack/lib/action_controller/deprecated/base.rb +++ b/actionpack/lib/action_controller/deprecated/base.rb @@ -77,14 +77,11 @@ def ip_spoofing_check def cookie_verifier_secret=(value) ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret= is deprecated. " << - "Please configure it on your application with config.cookie_secret=", caller - ActionController::Base.config.secret = value + "Please configure it on your application with config.secret_token=", caller end def cookie_verifier_secret - ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated. " << - "Please use ActionController::Base.config.secret instead.", caller - ActionController::Base.config.secret + ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated.", caller end def trusted_proxies=(value) diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index 4aaa705203..d787f014cd 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -10,8 +10,7 @@ module Cookies private def cookies - raise "You must set config.cookie_secret in your app's config" if config.secret.blank? - request.cookie_jar(:signing_secret => config.secret) + request.cookie_jar end end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 424828f7e8..6bd6c15990 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -159,7 +159,7 @@ def authenticate_or_request_with_http_digest(realm = "Application", &password_pr # Authenticate with HTTP Digest, returns true or false def authenticate_with_http_digest(realm = "Application", &password_procedure) - HttpAuthentication::Digest.authenticate(config.secret, request, realm, &password_procedure) + HttpAuthentication::Digest.authenticate(request, realm, &password_procedure) end # Render output including the HTTP Digest authentication header @@ -169,14 +169,15 @@ def request_http_digest_authentication(realm = "Application", message = nil) end # Returns false on a valid response, true otherwise - def authenticate(secret_key, request, realm, &password_procedure) - request.authorization && validate_digest_response(secret_key, request, realm, &password_procedure) + def authenticate(request, realm, &password_procedure) + request.authorization && validate_digest_response(request, realm, &password_procedure) end # Returns false unless the request credentials response value matches the expected value. # First try the password as a ha1 digest password. If this fails, then try it as a plain # text password. - def validate_digest_response(secret_key, request, realm, &password_procedure) + def validate_digest_response(request, realm, &password_procedure) + secret_key = secret_token(request) credentials = decode_credentials_header(request) valid_nonce = validate_nonce(secret_key, request, credentials[:nonce]) @@ -225,7 +226,7 @@ def decode_credentials(header) end def authentication_header(controller, realm) - secret_key = controller.config.secret + secret_key = secret_token(controller.request) nonce = self.nonce(secret_key) opaque = opaque(secret_key) controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") @@ -238,6 +239,12 @@ def authentication_request(controller, realm, message = nil) controller.status = 401 end + def secret_token(request) + secret = request.env["action_dispatch.secret_token"] + raise "You must set config.secret_token in your app's config" if secret.blank? + secret + end + # Uses an MD5 digest based on time to generate a value to be used only once. # # A server-specified data string which should be uniquely generated each time a 401 response is made. diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 29d8523ee1..030ba4ec48 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -51,7 +51,6 @@ class Railtie < Rails::Railtie ac.assets_dir = paths.public.to_a.first ac.javascripts_dir = paths.public.javascripts.to_a.first ac.stylesheets_dir = paths.public.stylesheets.to_a.first - ac.secret = app.config.cookie_secret ActiveSupport.on_load(:action_controller) do self.config.merge!(ac) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 71dcac9e94..42ab1d1ebb 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,7 +1,9 @@ +require "active_support/core_ext/object/blank" + module ActionDispatch class Request - def cookie_jar(config = {}) - env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self, config) + def cookie_jar + env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self) end end @@ -51,17 +53,17 @@ def cookie_jar(config = {}) # only HTTP. Defaults to +false+. class Cookies class CookieJar < Hash #:nodoc: - def self.build(request, config = {}) - new(config).tap do |hash| + def self.build(request) + secret = request.env["action_dispatch.secret_token"] + new(secret).tap do |hash| hash.update(request.cookies) end end - def initialize(config = {}) - @config = config + def initialize(secret=nil) + @secret = secret @set_cookies = {} @delete_cookies = {} - super() end @@ -112,7 +114,7 @@ def delete(key, options = {}) # cookies.permanent.signed[:remember_me] = current_user.id # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT def permanent - @permanent ||= PermanentCookieJar.new(self, @config) + @permanent ||= PermanentCookieJar.new(self, @secret) end # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from @@ -120,7 +122,7 @@ def permanent # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will # be raised. # - # This jar requires that you set a suitable secret for the verification on your app's config.cookie_secret. + # This jar requires that you set a suitable secret for the verification on your app's config.secret_token. # # Example: # @@ -129,7 +131,7 @@ def permanent # # cookies.signed[:discount] # => 45 def signed - @signed ||= SignedCookieJar.new(self, @config) + @signed ||= SignedCookieJar.new(self, @secret) end def write(response) @@ -139,9 +141,8 @@ def write(response) end class PermanentCookieJar < CookieJar #:nodoc: - def initialize(parent_jar, config = {}) - @parent_jar = parent_jar - @config = config + def initialize(parent_jar, secret) + @parent_jar, @secret = parent_jar, secret end def []=(key, options) @@ -156,7 +157,7 @@ def []=(key, options) end def signed - @signed ||= SignedCookieJar.new(self, @config) + @signed ||= SignedCookieJar.new(self, @secret) end def method_missing(method, *arguments, &block) @@ -165,11 +166,10 @@ def method_missing(method, *arguments, &block) end class SignedCookieJar < CookieJar #:nodoc: - def initialize(parent_jar, config = {}) - raise 'Missing cookie signing secret' if config[:signing_secret].blank? + def initialize(parent_jar, secret) + raise "You must set config.secret_token in your app's config" if secret.blank? @parent_jar = parent_jar - @config = config - @verifier = ActiveSupport::MessageVerifier.new(config[:signing_secret]) + @verifier = ActiveSupport::MessageVerifier.new(secret) end def [](name) diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 3331b7c25e..88ba941676 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -192,7 +192,7 @@ def ensure_secret_secure(secret) if secret.blank? raise ArgumentError, "A secret is required to generate an " + "integrity hash for cookie session data. Use " + - "config.cookie_secret = \"some secret phrase of at " + + "config.secret_token = \"some secret phrase of at " + "least #{SECRET_MIN_LENGTH} characters\"" + "in config/application.rb" end diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb index 090e03cf44..b3e67f6e36 100644 --- a/actionpack/lib/action_dispatch/testing/test_request.rb +++ b/actionpack/lib/action_dispatch/testing/test_request.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/hash/reverse_merge' module ActionDispatch class TestRequest < Request @@ -9,6 +10,7 @@ def self.new(env = {}) end def initialize(env = {}) + env = Rails.application.env_defaults.merge(env) if defined?(Rails.application) super(DEFAULT_ENV.merge(env)) self.host = 'test.host' diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index fe78b8ec1f..acf887ee4a 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -192,10 +192,6 @@ def with_routing(&block) # Temporary base class class Rack::TestCase < ActionController::IntegrationTest - setup do - ActionController::Base.config.secret = "abc" * 30 - end - def self.testing(klass = nil) if klass @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '') diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index 278cae1415..4971866e7c 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -1,7 +1,5 @@ require 'abstract_unit' -ActionController::Base.config.secret = "thisISverySECRET123" - class CookieTest < ActionController::TestCase class TestController < ActionController::Base def authenticate @@ -76,6 +74,7 @@ def delete_and_set_cookie def setup super + @request.env["action_dispatch.secret_token"] = "thisISverySECRET123" @request.host = "www.nextangle.com" end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index eb2af523a2..b011536717 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -41,8 +41,7 @@ def authenticate_with_request setup do # Used as secret in generating nonce to prevent tampering of timestamp @secret = "session_options_secret" - @controller.config.secret = @secret - # @old_secret, ActionController::Base.config.secret[:secret] = ActionController::Base.session_options[:secret], @secret + @request.env["action_dispatch.secret_token"] = @secret end teardown do @@ -206,7 +205,7 @@ def authenticate_with_request test "validate_digest_response should fail with nil returning password_procedure" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil) - assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@secret, @request, "SuperSecret"){nil} + assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret"){nil} end private diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 24ba378efe..82684e4614 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,5 @@ +* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV] + *Rails 3.0.0 [beta 2] (April 1st, 2010)* * Session store configuration has changed [YK & CL] @@ -6,12 +8,11 @@ config.cookie_secret = "fdsfhisdghfidugnfdlg" * railtie_name and engine_name are deprecated. You can now add any object to - the configuration object: config.your_plugin = {} [JK] + the configuration object: config.your_plugin = {} [JV] * Added config.generators.templates to provide alternative paths for the generators to look for templates [JV] - *Rails 3.0.0 [beta 1] (February 4, 2010)* * Added "rake about" as a replacement for script/about [DHH] diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 0084309ea4..38a5aa8ca3 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/hash/reverse_merge' require 'fileutils' require 'rails/plugin' require 'rails/engine' @@ -128,8 +129,14 @@ def app end def call(env) - env["action_dispatch.parameter_filter"] = config.filter_parameters - app.call(env) + app.call(env.reverse_merge!(env_defaults)) + end + + def env_defaults + @env_defaults ||= { + "action_dispatch.parameter_filter" => config.filter_parameters, + "action_dispatch.secret_token" => config.secret_token + } end def initializers diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 5c7de616be..d3e4742e8a 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -6,7 +6,7 @@ class Configuration < ::Rails::Engine::Configuration include ::Rails::Configuration::Deprecated attr_accessor :allow_concurrency, :cache_classes, :cache_store, - :cookie_secret, :consider_all_requests_local, :dependency_loading, + :secret_token, :consider_all_requests_local, :dependency_loading, :filter_parameters, :log_level, :logger, :metals, :plugins, :preload_frameworks, :reload_engines, :reload_plugins, :serve_static_assets, :time_zone, :whiny_nils @@ -37,6 +37,7 @@ def paths paths.app.controllers << builtin_controller if builtin_controller paths.config.database "config/database.yml" paths.config.environment "config/environments", :glob => "#{Rails.env}.rb" + paths.lib.templates "lib/templates" paths.log "log/#{Rails.env}.log" paths.tmp "tmp" paths.tmp.cache "tmp/cache" @@ -123,7 +124,7 @@ def session_store(*args) def session_options return @session_options unless @session_store == :cookie_store - @session_options.merge(:secret => @cookie_secret) + @session_options.merge(:secret => @secret_token) end def default_middleware_stack diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb index 978490f25f..94507bb387 100644 --- a/railties/lib/rails/application/finisher.rb +++ b/railties/lib/rails/application/finisher.rb @@ -3,6 +3,10 @@ class Application module Finisher include Initializable + initializer :add_generator_templates do + config.generators.templates.unshift(*paths.lib.templates.to_a) + end + initializer :ensure_load_once_paths_as_subset do extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 73ae9bcb16..dfd849b4bb 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -104,6 +104,18 @@ def controller_paths "please do paths.app.controllers instead", caller paths.app.controllers.to_a.uniq end + + def cookie_secret=(value) + ActiveSupport::Deprecation.warn "config.cookie_secret= is deprecated, " << + "please use config.secret_token= instead", caller + self.secret_token = value + end + + def cookie_secret + ActiveSupport::Deprecation.warn "config.cookie_secret is deprecated, " << + "please use config.secret_token instead", caller + self.secret_token + end end end end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index e9013348b5..54c97258ce 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -193,17 +193,13 @@ def load_tasks app.metal_loader.paths.unshift(*paths.app.metals.to_a) end - initializer :add_generator_templates do |app| - config.generators.templates.unshift(*paths.lib.templates.to_a) - end - - initializer :load_application_initializers do + initializer :load_config_initializers do paths.config.initializers.to_a.sort.each do |initializer| load(initializer) end end - initializer :load_application_classes do |app| + initializer :load_app_classes do |app| next if $rails_rake_task if app.config.cache_classes diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index b8f1f1009c..2129e10af8 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -23,7 +23,6 @@ def paths paths.app.views "app/views", :eager_load => true paths.lib "lib", :load_path => true paths.lib.tasks "lib/tasks", :glob => "**/*.rake" - paths.lib.templates "lib/templates" paths.config "config" paths.config.initializers "config/initializers", :glob => "**/*.rb" paths.config.locales "config/locales", :glob => "*.{rb,yml}" diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt similarity index 84% rename from railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt rename to railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt index be627fbbcc..c2fa31aadb 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/cookie_verification_secret.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt @@ -4,4 +4,4 @@ # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -Rails.application.config.cookie_secret = '<%= app_secret %>' +Rails.application.config.secret_token = '<%= app_secret %>' diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt index 9e32fb930e..a869a21e2c 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt @@ -1,8 +1,6 @@ # Be sure to restart your server when you modify this file. -Rails.application.config.session_store :cookie_store, { - :key => '_<%= app_name %>_session', -} +Rails.application.config.session_store :cookie_store, :key => '_<%= app_name %>_session' # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 0997be1b6f..fcdd099135 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -61,7 +61,7 @@ def config @config ||= Engine::Configuration.new end - initializer :load_init_rb, :before => :load_application_initializers do |app| + initializer :load_init_rb, :before => :load_config_initializers do |app| files = %w(rails/init.rb init.rb).map { |path| File.expand_path path, root } if initrb = files.find { |path| File.file? path } if initrb == files.first diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 68ca2acaad..90f2e2b370 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -234,6 +234,22 @@ def index assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"] end + test "config.secret_token is sent in env" do + make_basic_app do |app| + app.config.secret_token = 'ThisIsASECRET123' + end + + class ::OmgController < ActionController::Base + def index + cookies.signed[:some_key] = "some_value" + render :text => env["action_dispatch.secret_token"] + end + end + + get "/" + assert_equal 'ThisIsASECRET123', last_response.body + end + test "protect from forgery is the default in a new app" do make_basic_app diff --git a/railties/test/application/middleware_stack_defaults_test.rb b/railties/test/application/middleware_stack_defaults_test.rb index 284f7e2e5b..f31ca01fbf 100644 --- a/railties/test/application/middleware_stack_defaults_test.rb +++ b/railties/test/application/middleware_stack_defaults_test.rb @@ -10,7 +10,7 @@ def setup Object.const_set(:MyApplication, Class.new(Rails::Application)) MyApplication.class_eval do - config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, :key => "_myapp_session" end end diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb index 04f5454e09..72cae23985 100644 --- a/railties/test/application/url_generation_test.rb +++ b/railties/test/application/url_generation_test.rb @@ -14,7 +14,7 @@ def app require "action_controller/railtie" class MyApp < Rails::Application - config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, :key => "_myapp_session" end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 8f2f15b49e..e6896a1629 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -100,7 +100,7 @@ def build_app(options = {}) end end - add_to_config 'config.cookie_secret = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"' + add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"' end class Bukkit