aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/callbacks.rb55
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb60
2 files changed, 67 insertions, 48 deletions
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 656cba625c..ea37355fc1 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -153,7 +153,7 @@ module ActiveSupport
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def _one_time_conditions_valid_#{@callback_id}?
- true #{key_options[0]}
+ true if #{key_options}
end
RUBY_EVAL
end
@@ -171,8 +171,8 @@ module ActiveSupport
# if condition # before_save :filter_name, :if => :condition
# filter_name
# end
- filter = <<-RUBY_EVAL
- unless halted
+ <<-RUBY_EVAL
+ if !halted && #{@compiled_options}
# This double assignment is to prevent warnings in 1.9.3. I would
# remove the `result` variable, but apparently some other
# generated code is depending on this variable being set sometimes
@@ -181,8 +181,6 @@ module ActiveSupport
halted = (#{chain.config[:terminator]})
end
RUBY_EVAL
-
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
when :around
# Compile around filters with conditions into proxy methods
# that contain the conditions.
@@ -202,7 +200,7 @@ module ActiveSupport
name = "_conditional_callback_#{@kind}_#{next_id}"
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}(halted)
- #{@compiled_options[0] || "if true"} && !halted
+ if #{@compiled_options} && !halted
#{@filter} do
yield self
end
@@ -222,10 +220,12 @@ module ActiveSupport
case @kind
when :after
- # if condition # after_save :filter_name, :if => :condition
- # filter_name
- # end
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
+ # after_save :filter_name, :if => :condition
+ <<-RUBY_EVAL
+ if #{@compiled_options}
+ #{@filter}
+ end
+ RUBY_EVAL
when :around
<<-RUBY_EVAL
value
@@ -240,9 +240,7 @@ module ActiveSupport
# symbols, string, procs, and objects), so compile a conditional
# expression based on the options
def _compile_options(options)
- return [] if options[:if].empty? && options[:unless].empty?
-
- conditions = []
+ conditions = ["true"]
unless options[:if].empty?
conditions << Array.wrap(_compile_filter(options[:if]))
@@ -252,7 +250,7 @@ module ActiveSupport
conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
end
- ["if #{conditions.flatten.join(" && ")}", "end"]
+ conditions.flatten.join(" && ")
end
# Filters support:
@@ -371,42 +369,37 @@ module ActiveSupport
# Generate the internal runner method called by +run_callbacks+.
def __define_runner(symbol) #:nodoc:
body = send("_#{symbol}_callbacks").compile
+ runner_method = "_run_#{symbol}_callbacks"
silence_warnings do
- undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
+ undef_method runner_method if method_defined?(runner_method)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _run_#{symbol}_callbacks(key = nil, &blk)
+ def #{runner_method}(key = nil, &blk)
if key
- name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
-
- unless respond_to?(name)
- self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
- end
-
- send(name, &blk)
+ self.class.__run_keyed_callback(key, :#{symbol}, self, &blk)
else
#{body}
end
end
- private :_run_#{symbol}_callbacks
+ private :#{runner_method}
RUBY_EVAL
end
end
- # This is called the first time a callback is called with a particular
- # key. It creates a new callback method for the key, calculating
- # which callbacks can be omitted because of per_key conditions.
+ # This method calls the callback method for the given key.
+ # If this called first time it creates a new callback method for the key,
+ # calculating which callbacks can be omitted because of per_key conditions.
#
- def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
- @_keyed_callbacks ||= {}
- @_keyed_callbacks[name] ||= begin
+ def __run_keyed_callback(key, kind, object, &blk) #:nodoc:
+ name = "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks"
+ unless object.respond_to?(name)
str = send("_#{kind}_callbacks").compile(name, object)
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}() #{str} end
protected :#{name}
RUBY_EVAL
- true
end
+ object.send(name, &blk)
end
# This is used internally to append, prepend and skip callbacks to the
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index e14386a85d..9ef2b29580 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -10,6 +10,16 @@ module ActiveSupport
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't
# want users to be able to determine the value of the payload.
class MessageEncryptor
+ module NullSerializer #:nodoc:
+ def self.load(value)
+ value
+ end
+
+ def self.dump(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
@@ -18,13 +28,40 @@ module ActiveSupport
ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :cipher => 'algorithm' to specify the cipher algorithm."
options = { :cipher => options }
end
-
+
@secret = secret
@cipher = options[:cipher] || 'aes-256-cbc'
+ @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
@serializer = options[:serializer] || Marshal
end
def encrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#encrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#encrypt_and_sign instead."
+ _encrypt(value)
+ end
+
+ def decrypt(value)
+ ActiveSupport::Deprecation.warn "MessageEncryptor#decrypt is deprecated as it is not safe without a signature. " \
+ "Please use MessageEncryptor#decrypt_and_verify instead."
+ _decrypt(value)
+ end
+
+ # Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def encrypt_and_sign(value)
+ verifier.generate(_encrypt(value))
+ end
+
+ # Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
+ # Reference: http://www.limited-entropy.com/padding-oracle-attacks
+ def decrypt_and_verify(value)
+ _decrypt(verifier.verify(value))
+ end
+
+ private
+
+ def _encrypt(value)
cipher = new_cipher
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
@@ -39,7 +76,7 @@ module ActiveSupport
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
end
- def decrypt(encrypted_message)
+ def _decrypt(encrypted_message)
cipher = new_cipher
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
@@ -55,23 +92,12 @@ module ActiveSupport
raise InvalidMessage
end
- def encrypt_and_sign(value)
- verifier.generate(encrypt(value))
+ def new_cipher
+ OpenSSL::Cipher::Cipher.new(@cipher)
end
- def decrypt_and_verify(value)
- decrypt(verifier.verify(value))
+ def verifier
+ @verifier
end
-
-
-
- private
- def new_cipher
- OpenSSL::Cipher::Cipher.new(@cipher)
- end
-
- def verifier
- MessageVerifier.new(@secret)
- end
end
end