diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index 6cdb28ad50..b3cd560de0 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -124,6 +124,17 @@ See [active_record/enum.rb](https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/enum.rb#L2-L42) for a detailed write up. +### Application message verifier. + +Create a message verifier that can be used to generate and verify signed +messages in the application. + +```ruby +message = Rails.application.message_verifier('salt').generate('my sensible data') +Rails.application.message_verifier('salt').verify(message) +# => 'my sensible data' +``` + Documentation ------------- @@ -164,6 +175,7 @@ for detailed changes. * Exposed `MiddlewareStack#unshift` to environment configuration. ([Pull Request](https://github.com/rails/rails/pull/12479)) +* Add `Application#message_verifier` method to return a message verifier. ([Pull Request](https://github.com/rails/rails/pull/12995)) Action Mailer ------------- diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 0eda858cca..166080c5b0 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,20 @@ +* Add `Application#message_verifier` method to return a message verifier. + + This verifier can be used to generate and verify signed messages in the application. + + message = Rails.application.message_verifier('salt').generate('my sensible data') + Rails.application.message_verifier('salt').verify(message) + # => 'my sensible data' + + It is recommended not to use the same verifier for different things, so you can get different + verifiers passing the name argument. + + message = Rails.application.message_verifier('cookies').generate('my sensible cookie data') + + See the `ActiveSupport::MessageVerifier` documentation for more information. + + *Rafael Mendonça França* + * The [Spring application preloader](https://github.com/jonleighton/spring) is now installed by default for new applications. It uses the development group of diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index d1e88cfafd..e45bfaf6fc 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,6 +1,7 @@ require 'fileutils' require 'active_support/core_ext/object/blank' require 'active_support/key_generator' +require 'active_support/message_verifier' require 'rails/engine' module Rails @@ -107,12 +108,13 @@ module Rails def initialize(initial_variable_values = {}, &block) super() - @initialized = false - @reloaders = [] - @routes_reloader = nil - @app_env_config = nil - @ordered_railties = nil - @railties = nil + @initialized = false + @reloaders = [] + @routes_reloader = nil + @app_env_config = nil + @ordered_railties = nil + @railties = nil + @message_verifiers = {} add_lib_to_load_path! ActiveSupport.run_load_hooks(:before_configuration, self) @@ -158,6 +160,31 @@ module Rails end 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 + # verifiers passing the +verifier_name+ argument. + # + # ==== Parameters + # + # * +salt+ - the salt that will be used to generate the secret key of the verifier. + # + # ==== Examples + # + # message = Rails.application.message_verifier('salt').generate('my sensible data') + # Rails.application.message_verifier('salt').verify(message) + # # => 'my sensible data' + # + # See the +ActiveSupport::MessageVerifier+ documentation for more information. + def message_verifier(salt) + @message_verifiers[salt] ||= begin + secret = key_generator.generate_key(salt) + ActiveSupport::MessageVerifier.new(secret) + end + end + # Stores some of the Rails initial environment parameters which # will be used by middlewares and engines to configure themselves. def env_config diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 03a735b1c1..722c940d8e 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -268,6 +268,41 @@ module ApplicationTests assert_equal 'some_value', verifier.verify(last_response.body) end + test "application verifier can be used in the entire application" do + make_basic_app do |app| + app.config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33' + app.config.session_store :disabled + end + + message = app.message_verifier('salt').generate("some_value") + + assert_equal 'some_value', Rails.application.message_verifier('salt').verify(message) + + secret = app.key_generator.generate_key('salt') + verifier = ActiveSupport::MessageVerifier.new(secret) + assert_equal 'some_value', verifier.verify(message) + end + + test "application verifier can build different verifiers" do + make_basic_app do |app| + app.config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33' + app.config.session_store :disabled + end + + default_verifier = app.message_verifier('salt') + text_verifier = app.message_verifier('text') + + message = text_verifier.generate('some_value') + + assert_equal 'some_value', text_verifier.verify(message) + assert_raises ActiveSupport::MessageVerifier::InvalidSignature do + default_verifier.verify(message) + end + + assert_equal default_verifier.object_id, app.message_verifier('salt').object_id + assert_not_equal default_verifier.object_id, text_verifier.object_id + end + test "protect from forgery is the default in a new app" do make_basic_app