<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -70,6 +70,9 @@ current user's branch.&quot;
 
 = Examples
 
+A fully functional example application can be found at
+http://github.com/stffn/decl_auth_demo_app
+
 == Controller
 
 If authentication is in place, enabling user-specific access control may be</diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -110,13 +110,6 @@ module Authorization
       # find a authorization rule that matches for at least one of the roles and 
       # at least one of the given privileges
       attr_validator = AttributeValidator.new(self, user, options[:object])
-      #puts &quot;All rules: #{@auth_rules.inspect}&quot;
-      #rules_matching_roles = @auth_rules.select {|r| roles.include?(r.role) }
-      #puts &quot;Matching for roles: #{rules_matching_roles.inspect}&quot;
-      #puts &quot;Matching rules for user   #{user.inspect},&quot;
-      #puts &quot;                   roles  #{roles.inspect},&quot;
-      #puts &quot;                   privs  #{privileges.inspect}:&quot;
-      #puts &quot;   #{matching_auth_rules(roles, privileges).inspect}&quot;
       rules = matching_auth_rules(roles, privileges, options[:context])
       if rules.empty?
         raise NotAuthorized, &quot;No matching rules found for #{privilege} for #{user.inspect} &quot; +
@@ -336,10 +329,16 @@ module Authorization
           case value[0]
           when :is
             attr_value == evaluated
+          when :is_not
+            attr_value != evaluated
           when :contains
             attr_value.include?(evaluated)
+          when :does_not_contain
+            !attr_value.include?(evaluated)
           when :is_in
             evaluated.include?(attr_value)
+          when :is_not_in
+            !evaluated.include?(attr_value)
           else
             raise AuthorizationError, &quot;Unknown operator #{value[0]}&quot;
           end</diff>
      <filename>lib/authorization.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,22 +7,31 @@ module Authorization
   # constructs a data model of its contents.
   # 
   # For examples and the modelled data model, see the 
-  # README[link:files/README.html].
+  # README[link:files/README_rdoc.html].
   #
-  # Also, see 
+  # Also, see role definition methods
   # * AuthorizationRulesReader#role,
   # * AuthorizationRulesReader#includes,
   # * AuthorizationRulesReader#title,
-  # * AuthorizationRulesReader#description,
+  # * AuthorizationRulesReader#description
+  #
+  # Methods for rule definition in roles
   # * AuthorizationRulesReader#has_permission_on,
   # * AuthorizationRulesReader#to,
   # * AuthorizationRulesReader#if_attribute,
-  # * AuthorizationRulesReader#if_permitted_to,
+  # * AuthorizationRulesReader#if_permitted_to
+  #
+  # Methods to be used in if_attribute statements
   # * AuthorizationRulesReader#contains,
+  # * AuthorizationRulesReader#does_not_contain,
   # * AuthorizationRulesReader#is,
-  # * PrivilegesReader#privilege and
+  # * AuthorizationRulesReader#is_not,
+  # * AuthorizationRulesReader#is_in,
+  # * AuthorizationRulesReader#is_not_in
+  #
+  # And privilege definition methods
+  # * PrivilegesReader#privilege,
   # * PrivilegesReader#includes
-  # for details.
   #
   module Reader
     # Signals errors that occur while reading and parsing an authorization DSL
@@ -326,19 +335,29 @@ module Authorization
             attr_or_hash, options[:context])
       end
       
-      # In an if_attribute statement, is says that the value has to be exactly
-      # met by the if_attribute attribute.  For information on the block 
+      # In an if_attribute statement, is says that the value has to be
+      # met exactly by the if_attribute attribute.  For information on the block
       # argument, see if_attribute.
       def is (&amp;block)
         [:is, block]
       end
-      
+
+      # The negation of is.
+      def is_not (&amp;block)
+        [:is_not, block]
+      end
+
       # In an if_attribute statement, contains says that the value has to be
       # part of the collection specified by the if_attribute attribute.
       # For information on the block argument, see if_attribute.
       def contains (&amp;block)
         [:contains, block]
       end
+
+      # The negation of contains.
+      def does_not_contain (&amp;block)
+        [:does_not_contain, block]
+      end
       
       # In an if_attribute statement, is_in says that the value has to
       # contain the attribute value.
@@ -346,6 +365,11 @@ module Authorization
       def is_in (&amp;block)
         [:is_in, block]
       end
+
+      # The negation of is_in.
+      def is_not_in (&amp;block)
+        [:is_not_in, block]
+      end
       
       private
       def parse_attribute_conditions_hash! (hash)</diff>
      <filename>lib/reader.rb</filename>
    </modified>
    <modified>
      <diff>@@ -286,6 +286,26 @@ class AuthorizationTest &lt; Test::Unit::TestCase
               :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 2),
               :object =&gt; MockDataObject.new(:test_attr =&gt; 1)))))
   end
+
+  def test_attribute_is_not
+    reader = Authorization::Reader::DSLReader.new
+    reader.parse %|
+      authorization do
+        role :test_role do
+          has_permission_on :permissions, :to =&gt; :test do
+            if_attribute :test_attr =&gt; is_not { user.test_attr }
+          end
+        end
+      end
+    |
+    engine = Authorization::Engine.new(reader)
+    assert !engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 1),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; 1))
+    assert engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 2),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; 1))
+  end
   
   def test_attribute_contains
     reader = Authorization::Reader::DSLReader.new
@@ -306,6 +326,26 @@ class AuthorizationTest &lt; Test::Unit::TestCase
               :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 3),
               :object =&gt; MockDataObject.new(:test_attr =&gt; [1,2]))
   end
+
+  def test_attribute_does_not_contain
+    reader = Authorization::Reader::DSLReader.new
+    reader.parse %|
+      authorization do
+        role :test_role do
+          has_permission_on :permissions, :to =&gt; :test do
+            if_attribute :test_attr =&gt; does_not_contain { user.test_attr }
+          end
+        end
+      end
+    |
+    engine = Authorization::Engine.new(reader)
+    assert !engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 1),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; [1,2]))
+    assert engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role, :test_attr =&gt; 3),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; [1,2]))
+  end
   
   def test_attribute_in_array
     reader = Authorization::Reader::DSLReader.new
@@ -330,6 +370,26 @@ class AuthorizationTest &lt; Test::Unit::TestCase
               :user =&gt; MockUser.new(:test_role),
               :object =&gt; MockDataObject.new(:test_attr =&gt; 4))
   end
+
+  def test_attribute_not_in_array
+    reader = Authorization::Reader::DSLReader.new
+    reader.parse %|
+      authorization do
+        role :test_role do
+          has_permission_on :permissions, :to =&gt; :test do
+            if_attribute :test_attr =&gt; is_not_in { [1,2] }
+          end
+        end
+      end
+    |
+    engine = Authorization::Engine.new(reader)
+    assert !engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; 1))
+    assert engine.permit?(:test, :context =&gt; :permissions,
+              :user =&gt; MockUser.new(:test_role),
+              :object =&gt; MockDataObject.new(:test_attr =&gt; 4))
+  end
   
   def test_attribute_deep
     reader = Authorization::Reader::DSLReader.new</diff>
      <filename>test/authorization_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -84,7 +84,12 @@ class DSLReaderTest &lt; Test::Unit::TestCase
       authorization do
         role :test_role do
           has_permission_on :perms, :to =&gt; :test do
-            if_attribute :test_attr =&gt; is { user.test_attr }
+            if_attribute :test_attr   =&gt; is { user.test_attr }
+            if_attribute :test_attr_2 =&gt; is_not { user.test_attr }
+            if_attribute :test_attr_3 =&gt; contains { user.test_attr }
+            if_attribute :test_attr_4 =&gt; does_not_contain { user.test_attr }
+            if_attribute :test_attr_5 =&gt; is_in { user.test_attr }
+            if_attribute :test_attr_5 =&gt; is_not_in { user.test_attr }
           end
         end
       end</diff>
      <filename>test/dsl_reader_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ed0f9c369f8fb9db19e42f06e3c8fd50bd4f3453</id>
    </parent>
  </parents>
  <author>
    <name>Steffen Bartsch</name>
    <email>sbartsch@tzi.org</email>
  </author>
  <url>http://github.com/stffn/declarative_authorization/commit/651fe2b8f42a0661f6775c1982699e4c75470658</url>
  <id>651fe2b8f42a0661f6775c1982699e4c75470658</id>
  <committed-date>2009-02-02T11:01:20-08:00</committed-date>
  <authored-date>2009-02-02T11:01:20-08:00</authored-date>
  <message>New if_attribute operators: is_not, does_not_contain, not_in.</message>
  <tree>470a4605983d9299b70dd15ee64d3fafcc743a9b</tree>
  <committer>
    <name>Steffen Bartsch</name>
    <email>sbartsch@tzi.org</email>
  </committer>
</commit>
