diff options
-rw-r--r-- | activemodel/CHANGELOG.md | 5 | ||||
-rw-r--r-- | activemodel/lib/active_model/errors.rb | 13 | ||||
-rw-r--r-- | activemodel/lib/active_model/locale/en.yml | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/validations/absence.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/validator.rb | 2 | ||||
-rw-r--r-- | activerecord/test/models/person.rb | 2 | ||||
-rw-r--r-- | activesupport/CHANGELOG.md | 9 | ||||
-rw-r--r-- | activesupport/lib/active_support/callbacks.rb | 34 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/logger.rb | 2 | ||||
-rw-r--r-- | activesupport/test/callbacks_test.rb | 62 | ||||
-rw-r--r-- | activesupport/test/test_test.rb | 10 | ||||
-rw-r--r-- | guides/source/security.md | 20 |
12 files changed, 118 insertions, 47 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index b1d33c8692..09e6ede064 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -19,11 +19,6 @@ *Roberto Vasquez Angel* -* Added `ActiveModel::Errors#add_on_present` method. Adds error messages to - present attributes. - - *Roberto Vasquez Angel* - * `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!` *Renato Mascarenhas* diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index b713e99e25..963e52bff3 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -328,19 +328,6 @@ module ActiveModel end end - # Will add an error message to each of the attributes in +attributes+ that - # is present (using Object#present?). - # - # person.errors.add_on_present(:name) - # person.errors.messages - # # => { :name => ["must be blank"] } - def add_on_present(attributes, options = {}) - Array(attributes).flatten.each do |attribute| - value = @base.send(:read_attribute_for_validation, attribute) - add(attribute, :not_blank, options) if value.present? - end - end - # Returns +true+ if an error on the attribute with the given message is # present, +false+ otherwise. +message+ is treated the same as for +add+. # diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 8ea34b84f5..540e8132d3 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -13,7 +13,7 @@ en: accepted: "must be accepted" empty: "can't be empty" blank: "can't be blank" - not_blank: "must be blank" + present: "must be blank" too_long: "is too long (maximum is %{count} characters)" too_short: "is too short (minimum is %{count} characters)" wrong_length: "is the wrong length (should be %{count} characters)" diff --git a/activemodel/lib/active_model/validations/absence.rb b/activemodel/lib/active_model/validations/absence.rb index 6790554907..1a1863370b 100644 --- a/activemodel/lib/active_model/validations/absence.rb +++ b/activemodel/lib/active_model/validations/absence.rb @@ -2,8 +2,8 @@ module ActiveModel module Validations # == Active Model Absence Validator class AbsenceValidator < EachValidator #:nodoc: - def validate(record) - record.errors.add_on_present(attributes, options) + def validate_each(record, attr_name, value) + record.errors.add(attr_name, :present, options) if value.present? end end diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index ff3dfa01c8..d51f4d1936 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -110,7 +110,7 @@ module ActiveModel # Return the kind for this validator. # # PresenceValidator.new.kind # => :presence - # UniquenessValidator.new.kind # => :uniqueness + # UniquenessValidator.new.kind # => :uniqueness def kind self.class.kind end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index f9684b0bcd..c602ca5eac 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -106,7 +106,7 @@ class Insure def self.load mask INSURES.select do |insure| - (1 << INSURES.index(insure)) & mask > 0 + (1 << INSURES.index(insure)) & mask.to_i > 0 end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 1d11ae2afd..41fde553fb 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,14 @@ ## Rails 4.0.0 (unreleased) ## +* Prevent `Callbacks#set_callback` from setting the same callback twice. + + before_save :foo, :bar, :foo + + will at first call `bar`, then `foo`. `foo` will no more be called + twice. + + *Dmitriy Kiriyenko* + * Add ActiveSupport::Logger#silence that works the same as the old Logger#silence extension. *DHH* diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index e772a297fc..e3e1845868 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -134,6 +134,10 @@ module ActiveSupport @kind == _kind && @filter == _filter end + def duplicates?(other) + matches?(other.kind, other.filter) + end + def _update_filter(filter_options, new_options) filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless) filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if) @@ -328,6 +332,30 @@ module ActiveSupport method.join("\n") end + def append(*callbacks) + callbacks.each { |c| append_one(c) } + end + + def prepend(*callbacks) + callbacks.each { |c| prepend_one(c) } + end + + private + + def append_one(callback) + remove_duplicates(callback) + push(callback) + end + + def prepend_one(callback) + remove_duplicates(callback) + unshift(callback) + end + + def remove_duplicates(callback) + delete_if { |c| callback.duplicates?(c) } + end + end module ClassMethods @@ -421,11 +449,7 @@ module ActiveSupport Callback.new(chain, filter, type, options.dup, self) end - filters.each do |filter| - chain.delete_if {|c| c.matches?(type, filter) } - end - - options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped) + options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) target.send("_#{name}_callbacks=", chain) end diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index 36c8f241b2..34de766331 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -32,7 +32,7 @@ require 'logger' # # logger.datetime_format = "%Y-%m-%d" # -# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger +# Note: This logger is deprecated in favor of ActiveSupport::Logger class Logger include LoggerSilence diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 8810302f40..13f2e3cdaf 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -297,7 +297,7 @@ module CallbacksTest end end end - + class AroundPersonResult < MySuper attr_reader :result @@ -308,7 +308,7 @@ module CallbacksTest def tweedle_dum @result = yield end - + def tweedle_1 :tweedle_1 end @@ -316,7 +316,7 @@ module CallbacksTest def tweedle_2 :tweedle_2 end - + def save run_callbacks :save do :running @@ -410,7 +410,7 @@ module CallbacksTest ], around.history end end - + class AroundCallbackResultTest < ActiveSupport::TestCase def test_save_around around = AroundPersonResult.new @@ -607,6 +607,45 @@ module CallbacksTest end end + class OneTwoThreeSave + include ActiveSupport::Callbacks + + define_callbacks :save + + attr_accessor :record + + def initialize + @record = [] + end + + def save + run_callbacks :save do + @record << "yielded" + end + end + + def first + @record << "one" + end + + def second + @record << "two" + end + + def third + @record << "three" + end + end + + class DuplicatingCallbacks < OneTwoThreeSave + set_callback :save, :before, :first, :second + set_callback :save, :before, :first, :third + end + + class DuplicatingCallbacksInSameCall < OneTwoThreeSave + set_callback :save, :before, :first, :second, :first, :third + end + class UsingObjectTest < ActiveSupport::TestCase def test_before_object u = UsingObjectBefore.new @@ -709,5 +748,18 @@ module CallbacksTest end end end - + + class ExcludingDuplicatesCallbackTest < ActiveSupport::TestCase + def test_excludes_duplicates_in_separate_calls + model = DuplicatingCallbacks.new + model.save + assert_equal ["two", "one", "three", "yielded"], model.record + end + + def test_excludes_duplicates_in_one_call + model = DuplicatingCallbacksInSameCall.new + model.save + assert_equal ["two", "one", "three", "yielded"], model.record + end + end end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index 9516556844..d6821135ea 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -122,12 +122,12 @@ end # Setup and teardown callbacks. class SetupAndTeardownTest < ActiveSupport::TestCase setup :reset_callback_record, :foo - teardown :foo, :sentinel, :foo + teardown :foo, :sentinel def test_inherited_setup_callbacks assert_equal [:reset_callback_record, :foo], self.class._setup_callbacks.map(&:raw_filter) assert_equal [:foo], @called_back - assert_equal [:foo, :sentinel, :foo], self.class._teardown_callbacks.map(&:raw_filter) + assert_equal [:foo, :sentinel], self.class._teardown_callbacks.map(&:raw_filter) end def setup @@ -147,7 +147,7 @@ class SetupAndTeardownTest < ActiveSupport::TestCase end def sentinel - assert_equal [:foo, :foo], @called_back + assert_equal [:foo], @called_back end end @@ -159,7 +159,7 @@ class SubclassSetupAndTeardownTest < SetupAndTeardownTest def test_inherited_setup_callbacks assert_equal [:reset_callback_record, :foo, :bar], self.class._setup_callbacks.map(&:raw_filter) assert_equal [:foo, :bar], @called_back - assert_equal [:foo, :sentinel, :foo, :bar], self.class._teardown_callbacks.map(&:raw_filter) + assert_equal [:foo, :sentinel, :bar], self.class._teardown_callbacks.map(&:raw_filter) end protected @@ -168,7 +168,7 @@ class SubclassSetupAndTeardownTest < SetupAndTeardownTest end def sentinel - assert_equal [:foo, :bar, :bar, :foo], @called_back + assert_equal [:foo, :bar, :bar], @called_back end end diff --git a/guides/source/security.md b/guides/source/security.md index 8096ea2383..0b0cfe69c4 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -94,16 +94,15 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves * The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb: +That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. -```ruby -config.action_dispatch.session = { - key: '_app_session', - secret: '0x0dkfj3927dkc7djdh36rkckdfzsg...' -} -``` +`config.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_key_base` initialized to a random key in `config/initializers/secret_token.rb`, e.g.: + + YourApp::Application.config.secret_key_base = '49d3f3de9ed86c74b94ad6bd0...' -There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it. +Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information. + +If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. ### Replay Attacks for CookieStore Sessions @@ -959,6 +958,11 @@ Used to control which sites are allowed to bypass same origin policies and send * Strict-Transport-Security [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) +Environmental Security +---------------------- + +It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/initializers/secret_token.rb`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. + Additional Resources -------------------- |