Created
March 7, 2012 16:19
-
-
Save isokcevic/1994160 to your computer and use it in GitHub Desktop.
A matcher for whitelisted attributes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module WhitelistMatcher | |
# Checks if all of the specified attributes are mass-assignable, and if all the others are protected. | |
# If used with should_not, then ONLY the specified attributes should be protected from mass assignment, and for all other it should be allowed | |
# (i.e. behavior similar to :except ) | |
# It also supports roles by chaining as() | |
# | |
# it { should whitelist(:email, :password, :password_confirmation) } | |
# it { should_not whitelist(:admin, :balance) | |
# it { should whitelist(:owner).as(:superuser) | |
# | |
def whitelist(*attributes) | |
Whitelist.new(attributes) | |
end | |
class Whitelist | |
def initialize(attributes) | |
@attributes = attributes.collect{|a| a.to_s} | |
end | |
def as(role) | |
@role = role | |
self | |
end | |
# all the specified attributes must satisfy the following conditions: | |
# - writer method must exist | |
# - mass assignment must be allowed | |
# all the other attributes must be protected from mass assignment | |
def matches?(target) | |
setup_defaults(target) | |
@attributes.each do |attr| | |
if ! @writers.include?(attr) | |
@errors_for_should << "#{target.class.name} does not have the '#{attr}' attribute, but it was expected to be allowed for mass assignment" | |
@passed = false | |
elsif @authorizer.deny?(attr) | |
@errors_for_should << "#{target.class.name} does not allow mass assignment of '#{attr}' for role #{@role}, but it should" | |
@passed = false | |
end | |
end | |
@unspecified_attributes.each do |attr| | |
if ! @authorizer.deny?(attr) | |
@errors_for_should << "#{target.class.name} allows mass assignment of '#{attr}' for role #{@role}, but it is not expected" | |
@passed = false | |
end | |
end | |
@passed | |
end | |
# all the specified attributes must satisfy the following conditions: | |
# - writer method must exist | |
# - mass assignment must NOT be allowed | |
# all the other attributes must allow mass assignment | |
def does_not_match?(target) | |
setup_defaults(target) | |
@attributes.each do |attr| | |
if ! @writers.include?(attr) | |
@errors_for_should_not << "#{target.class.name} does not have the '#{attr}' attribute, but it was expected to be protected against mass assignment" | |
@passed = false | |
elsif ! @authorizer.deny?(attr) | |
@errors_for_should_not << "#{target.class.name} allows mass assignment of '#{attr}' for role #{@role}, but it should not" | |
@passed = false | |
end | |
end | |
@unspecified_attributes.each do |attr| | |
if @authorizer.deny?(attr) | |
@errors_for_should_not << "#{target.class.name} does not allow mass assignment of '#{attr}' for role #{@role}, but it is expected" | |
@passed = false | |
end | |
end | |
@passed | |
end | |
def failure_message_for_should | |
@errors_for_should.join("\n") | |
end | |
def failure_message_for_should_not | |
@errors_for_should_not.join("\n") | |
end | |
private | |
def setup_defaults(target) | |
@passed = true | |
@role ||= :default | |
@errors_for_should = [] | |
@errors_for_should_not = [] | |
@authorizer = target.class.active_authorizer[@role] | |
if !@authorizer | |
@errors_for_should << "Unknown role '#{@role}' for #{target.class.name}" | |
@errors_for_should_not << "Unknown role '#{@role}' for #{target.class.name}" | |
end | |
#get all the possible writers | |
@writers = ((target.class.instance_method_names - target.class.superclass.instance_method_names).select{|n| n=~ /=$/}.collect{|n| n[0..-2]} + target.class.column_names).uniq | |
#attributes not specified, which should behave opposite of those specified | |
@unspecified_attributes = @writers - @attributes | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment