Skip to content

Commit

Permalink
Provide a more robust behavior to serializers and add :force_except o…
Browse files Browse the repository at this point in the history
…ption
  • Loading branch information
josevalim committed Jun 30, 2011
1 parent 5a98e4f commit 33d7644
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rdoc
@@ -1,5 +1,6 @@
* bug fix
* Improve Rails 3.1 compatibility
* Provide a more robust behavior to serializers and add :force_except option

== 1.4.1

Expand Down
17 changes: 3 additions & 14 deletions lib/devise/models/authenticatable.rb
@@ -1,4 +1,5 @@
require 'devise/hooks/activatable'
require 'devise/models/serializable'

module Devise
module Models
Expand Down Expand Up @@ -46,6 +47,8 @@ module Models
module Authenticatable
extend ActiveSupport::Concern

include Devise::Models::Serializable

included do
class_attribute :devise_modules, :instance_writer => false
self.devise_modules ||= []
Expand Down Expand Up @@ -76,20 +79,6 @@ def inactive_message
def authenticatable_salt
end

# TODO: to_xml does not call serializable_hash. Hopefully someone will fix this in AR.
%w(to_xml serializable_hash).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__
def #{method}(options={})
if self.class.respond_to?(:accessible_attributes)
options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
super(options)
else
super
end
end
RUBY
end

module ClassMethods
Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)

Expand Down
43 changes: 43 additions & 0 deletions lib/devise/models/serializable.rb
@@ -0,0 +1,43 @@
module Devise
module Models
# This module redefine to_xml and serializable_hash in models for more
# secure defaults. By default, it removes from the serializable model
# all attributes that are *not* accessible. You can remove this default
# by using :force_except and passing a new list of attributes you want
# to exempt. All attributes given to :except will simply add names to
# exempt to Devise internal list.
module Serializable
extend ActiveSupport::Concern

# TODO: to_xml does not call serializable_hash. Hopefully someone will fix this in AR.
%w(to_xml serializable_hash).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__
def #{method}(options=nil)
options ||= {}
if options.key?(:force_except)
options[:except] = options.delete(:force_except)
super(options)
elsif self.class.blacklist_keys?
except = Array(options[:except])
super(options.merge(:except => except + self.class.blacklist_keys))
else
super
end
end
RUBY
end

module ClassMethods
# Return true if we can retrieve blacklist keys from the record.
def blacklist_keys?
@has_except_keys ||= respond_to?(:accessible_attributes) && !accessible_attributes.to_a.empty?
end

# Returns keys that should be removed when serializing the record.
def blacklist_keys
@blacklist_keys ||= to_adapter.column_names.map(&:to_s) - accessible_attributes.to_a.map(&:to_s)
end
end
end
end
end
38 changes: 38 additions & 0 deletions test/models/serializable_test.rb
@@ -0,0 +1,38 @@
require 'test_helper'

class SerializableTest < ActiveSupport::TestCase
setup do
@user = create_user
end

test 'should not include unsafe keys on XML' do
assert_match /email/, @user.to_xml
assert_no_match /confirmation-token/, @user.to_xml
end

test 'should not include unsafe keys on XML even if a new except is provided' do
assert_no_match /email/, @user.to_xml(:except => :email)
assert_no_match /confirmation-token/, @user.to_xml(:except => :email)
end

test 'should include unsafe keys on XML if a force_except is provided' do
assert_no_match /email/, @user.to_xml(:force_except => :email)
assert_match /confirmation-token/, @user.to_xml(:force_except => :email)
end

test 'should not include unsafe keys on JSON' do
assert_match /"email":/, @user.to_json
assert_no_match /"confirmation_token":/, @user.to_json
end

test 'should not include unsafe keys on JSON even if a new except is provided' do
assert_no_match /"email":/, @user.to_json(:except => :email)
assert_no_match /"confirmation_token":/, @user.to_json(:except => :email)
end

test 'should include unsafe keys on JSON if a force_except is provided' do
assert_no_match /"email":/, @user.to_json(:force_except => :email)
assert_match /"confirmation_token":/, @user.to_json(:force_except => :email)
end

end

0 comments on commit 33d7644

Please sign in to comment.