Switch to side-by-side view

--- a/Allura/allura/tests/test_security.py
+++ b/Allura/allura/tests/test_security.py
@@ -1,5 +1,24 @@
+from pylons import tmpl_context as c
+from nose.tools import assert_equal
+
+from ming.odm import ThreadLocalODMSession
 from allura.tests import decorators as td
 from allura.tests import TestController
+
+from allura.lib.security import Credentials, all_allowed, has_access
+from allura import model as M
+from forgewiki import model as WM
+
+
+def _deny(obj, role, perm):
+    obj.acl.insert(0, M.ACE.deny(role._id, perm))
+    ThreadLocalODMSession.flush_all()
+    Credentials.get().clear()
+
+def _add_to_group(user, role):
+    user.project_role().roles.append(role._id)
+    ThreadLocalODMSession.flush_all()
+    Credentials.get().clear()
 
 class TestSecurity(TestController):
 
@@ -25,3 +44,78 @@
         self.app.get('/security/test-admin/needs_artifact_access_fail', status=200)
         self.app.get('/security/test-admin/needs_artifact_access_ok', status=200)
 
+    @td.with_wiki
+    def test_all_allowed(self):
+        wiki = c.project.app_instance('wiki')
+        page = WM.Page.query.get(app_config_id=wiki.config._id)
+        admin_role = M.ProjectRole.by_name('Admin')
+        dev_role = M.ProjectRole.by_name('Developer')
+        member_role = M.ProjectRole.by_name('Member')
+        auth_role = M.ProjectRole.by_name('*authenticated')
+        anon_role = M.ProjectRole.by_name('*anonymous')
+        test_user = M.User.by_username('test-user')
+
+        assert_equal(all_allowed(wiki, admin_role), set(['configure', 'read', 'create', 'edit', 'unmoderated_post', 'post', 'moderate', 'admin', 'delete']))
+        assert_equal(all_allowed(wiki, dev_role), set(['read', 'create', 'edit', 'unmoderated_post', 'post', 'moderate', 'delete']))
+        assert_equal(all_allowed(wiki, member_role), set(['read', 'create', 'edit', 'unmoderated_post', 'post']))
+        assert_equal(all_allowed(wiki, auth_role), set(['read', 'post', 'unmoderated_post']))
+        assert_equal(all_allowed(wiki, anon_role), set(['read']))
+        assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))
+
+        _add_to_group(test_user, member_role)
+
+        assert_equal(all_allowed(wiki, test_user), set(['read', 'create', 'edit', 'unmoderated_post', 'post']))
+
+        _deny(wiki, auth_role, 'unmoderated_post')
+
+        assert_equal(all_allowed(wiki, member_role), set(['read', 'create', 'edit', 'post']))
+        assert_equal(all_allowed(wiki, test_user), set(['read', 'create', 'edit', 'post']))
+
+    @td.with_wiki
+    def test_weird_allow_vs_deny(self):
+        '''
+        Test weird interaction of DENYs and ALLOWs in has_access.
+        '''
+        wiki = c.project.app_instance('wiki')
+        page = WM.Page.query.get(app_config_id=wiki.config._id)
+        auth_role = M.ProjectRole.by_name('*authenticated')
+        test_user = M.User.by_username('test-user')
+
+
+        # DENY for auth_role on page prevents chaining of auth_role for 'read'
+        # but anon_role still chains so ALLOW read for anon_role on wiki applies
+        # and authed user can still read.  'post' and 'unmoderated_post' don't
+        # match DENY rule so they chain as normal.
+        #
+        # This behavior seems wrong and should probably be fixed at some point,
+        # but this test is here to confirm that all_allowed matches has_access.
+        assert has_access(page, 'read', test_user)()
+        assert has_access(page, 'post', test_user)()
+        assert has_access(page, 'unmoderated_post', test_user)()
+        assert_equal(all_allowed(page, test_user), set(['read', 'post', 'unmoderated_post']))
+
+        _deny(page, auth_role, 'read')
+
+        assert has_access(page, 'read', test_user)()
+        assert has_access(page, 'post', test_user)()
+        assert has_access(page, 'unmoderated_post', test_user)()
+        assert_equal(all_allowed(page, test_user), set(['read', 'post', 'unmoderated_post']))
+
+
+        # Same thing applies to ALLOW vs DENY on the same ACL;
+        # an ALLOW on any applicable role overrides a DENY on any other.
+        #
+        # In this case it's reasonable since you might want to DENY read for
+        # *anon but ALLOW it for *auth.  *anon ALLOW overriding *auth DENY is
+        # just an unfortunate side-effect of not having a true heiarchy of roles.
+        assert has_access(wiki, 'read', test_user)()
+        assert has_access(wiki, 'post', test_user)()
+        assert has_access(wiki, 'unmoderated_post', test_user)()
+        assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))
+
+        _deny(wiki, auth_role, 'read')
+
+        assert has_access(wiki, 'read', test_user)()
+        assert has_access(wiki, 'post', test_user)()
+        assert has_access(wiki, 'unmoderated_post', test_user)()
+        assert_equal(all_allowed(wiki, test_user), set(['read', 'post', 'unmoderated_post']))