Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

New operator: intersects_with for comparing collections with a set of…

… values
  • Loading branch information...
commit 29d81ec04f88e498af8623fc3c357b2111467873 1 parent 938bc7b
@stffn authored
View
2  CHANGELOG
@@ -1,3 +1,5 @@
+* New operator: intersects_with, comparing two Enumerables in if_attribute
+
* Improved if_permitted_to syntax: if the attribute is left out, permissions are checked on for the current object
* Added #has_role_with_hierarchy? method to retrieve explicit and calculated roles [jeremyf]
View
40 lib/declarative_authorization/authorization.rb
@@ -385,13 +385,45 @@ def validate? (attr_validator, object = nil, hash = nil)
when :is_not
attr_value != evaluated
when :contains
- attr_value.include?(evaluated)
+ begin
+ attr_value.include?(evaluated)
+ rescue NoMethodError => e
+ raise AuthorizationUsageError, "Operator contains requires a " +
+ "subclass of Enumerable as attribute value, got: #{attr_value.inspect} " +
+ "contains #{evaluated.inspect}: #{e}"
+ end
when :does_not_contain
- !attr_value.include?(evaluated)
+ begin
+ !attr_value.include?(evaluated)
+ rescue NoMethodError => e
+ raise AuthorizationUsageError, "Operator does_not_contain requires a " +
+ "subclass of Enumerable as attribute value, got: #{attr_value.inspect} " +
+ "does_not_contain #{evaluated.inspect}: #{e}"
+ end
+ when :intersects_with
+ begin
+ !(evaluated.to_set & attr_value.to_set).empty?
+ rescue NoMethodError => e
+ raise AuthorizationUsageError, "Operator intersects_with requires " +
+ "subclasses of Enumerable, got: #{attr_value.inspect} " +
+ "intersects_with #{evaluated.inspect}: #{e}"
+ end
when :is_in
- evaluated.include?(attr_value)
+ begin
+ evaluated.include?(attr_value)
+ rescue NoMethodError => e
+ raise AuthorizationUsageError, "Operator is_in requires a " +
+ "subclass of Enumerable as value, got: #{attr_value.inspect} " +
+ "is_in #{evaluated.inspect}: #{e}"
+ end
when :is_not_in
- !evaluated.include?(attr_value)
+ begin
+ !evaluated.include?(attr_value)
+ rescue NoMethodError => e
+ raise AuthorizationUsageError, "Operator is_not_in requires a " +
+ "subclass of Enumerable as value, got: #{attr_value.inspect} " +
+ "is_not_in #{evaluated.inspect}: #{e}"
+ end
else
raise AuthorizationError, "Unknown operator #{value[0]}"
end
View
3  lib/declarative_authorization/obligation_scope.rb
@@ -211,8 +211,9 @@ def rebuild_condition_options!
attribute_operator = case operator
when :contains, :is then "= :#{bindvar}"
when :does_not_contain, :is_not then "<> :#{bindvar}"
- when :is_in then "IN (:#{bindvar})"
+ when :is_in, :intersects_with then "IN (:#{bindvar})"
when :is_not_in then "NOT IN (:#{bindvar})"
+ else raise AuthorizationUsageError, "Unknown operator: #{operator}"
end
obligation_conds << "#{connection.quote_table_name(attribute_table_alias)}.#{connection.quote_table_name(attribute_name)} #{attribute_operator}"
binds[bindvar] = attribute_value(value)
View
12 lib/declarative_authorization/reader.rb
@@ -24,6 +24,7 @@ module Authorization
# Methods to be used in if_attribute statements
# * AuthorizationRulesReader#contains,
# * AuthorizationRulesReader#does_not_contain,
+ # * AuthorizationRulesReader#intersects_with,
# * AuthorizationRulesReader#is,
# * AuthorizationRulesReader#is_not,
# * AuthorizationRulesReader#is_in,
@@ -379,10 +380,19 @@ def contains (&block)
[:contains, block]
end
- # The negation of contains.
+ # The negation of contains. Currently, query rewriting is disabled
+ # for does_not_contain.
def does_not_contain (&block)
[:does_not_contain, block]
end
+
+ # In an if_attribute statement, intersects_with requires that at least
+ # one of the values has to be part of the collection specified by the
+ # if_attribute attribute. The value block needs to evaluate to an
+ # Enumerable. For information on the block argument, see if_attribute.
+ def intersects_with (&block)
+ [:intersects_with, block]
+ end
# In an if_attribute statement, is_in says that the value has to
# contain the attribute value.
View
36 test/authorization_test.rb
@@ -390,6 +390,42 @@ def test_attribute_not_in_array
:user => MockUser.new(:test_role),
:object => MockDataObject.new(:test_attr => 4))
end
+
+ def test_attribute_intersects_with
+ reader = Authorization::Reader::DSLReader.new
+ reader.parse %{
+ authorization do
+ role :test_role do
+ has_permission_on :permissions, :to => :test do
+ if_attribute :test_attrs => intersects_with { [1,2] }
+ end
+ end
+ role :test_role_2 do
+ has_permission_on :permissions, :to => :test do
+ if_attribute :test_attrs => intersects_with { 1 }
+ end
+ end
+ end
+ }
+
+ engine = Authorization::Engine.new(reader)
+ assert_raise Authorization::AuthorizationUsageError do
+ engine.permit?(:test, :context => :permissions,
+ :user => MockUser.new(:test_role),
+ :object => MockDataObject.new(:test_attrs => 1 ))
+ end
+ assert_raise Authorization::AuthorizationUsageError do
+ engine.permit?(:test, :context => :permissions,
+ :user => MockUser.new(:test_role_2),
+ :object => MockDataObject.new(:test_attrs => [1, 2] ))
+ end
+ assert engine.permit?(:test, :context => :permissions,
+ :user => MockUser.new(:test_role),
+ :object => MockDataObject.new(:test_attrs => [1,3] ))
+ assert !engine.permit?(:test, :context => :permissions,
+ :user => MockUser.new(:test_role),
+ :object => MockDataObject.new(:test_attrs => [3,4] ))
+ end
def test_attribute_deep
reader = Authorization::Reader::DSLReader.new
View
32 test/model_test.rb
@@ -586,6 +586,38 @@ def test_named_scope_with_contains_through_primary_key
TestAttr.delete_all
end
end
+
+ def test_named_scope_with_intersects_with
+ reader = Authorization::Reader::DSLReader.new
+ reader.parse %{
+ authorization do
+ role :test_role do
+ has_permission_on :test_models, :to => :read do
+ if_attribute :test_attrs => intersects_with { user.test_attrs }
+ end
+ end
+ end
+ }
+ Authorization::Engine.instance(reader)
+
+ test_model_1 = TestModel.create!
+ test_model_2 = TestModel.create!
+ test_model_1.test_attrs.create!
+ test_model_1.test_attrs.create!
+ test_model_1.test_attrs.create!
+ test_model_2.test_attrs.create!
+
+ user = MockUser.new(:test_role,
+ :test_attrs => [test_model_1.test_attrs.first, TestAttr.create!])
+ assert_equal 1, TestModel.with_permissions_to(:read, :user => user).length
+
+ user = MockUser.new(:test_role,
+ :test_attrs => [TestAttr.create!])
+ assert_equal 0, TestModel.with_permissions_to(:read, :user => user).length
+
+ TestModel.delete_all
+ TestAttr.delete_all
+ end
def test_named_scope_with_is_and_has_one
reader = Authorization::Reader::DSLReader.new
Please sign in to comment.
Something went wrong with that request. Please try again.