changeset 185:fa88b784cd08

split ACLs into ACLs to control functionality access and ACLs to control content access related wikiconfig attributes: acl_functions - Access Control List for functions acl_rights_contents - Valid tokens for right sides of content ACL entries acl_rights_functions - Valid tokens for right sides of function ACL entries FunctionACL - subclass of AccessControlList accepting stuff valid for function acls ContentACL - subclass of AccessControlList accepting stuff valid for content acls added some XXX comments about places needing improvements
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 23 Apr 2011 21:50:38 +0200
parents 8d574f93a6ef
children 5dd1db45ed55
files MoinMoin/apps/admin/views.py MoinMoin/config/__init__.py MoinMoin/config/default.py MoinMoin/datastruct/backends/_tests/__init__.py MoinMoin/datastruct/backends/_tests/test_wiki_groups.py MoinMoin/security/__init__.py MoinMoin/security/_tests/test_security.py MoinMoin/security/textcha.py MoinMoin/storage/_tests/test_backends_fs19.py MoinMoin/storage/backends/acl.py MoinMoin/storage/backends/fs19.py
diffstat 11 files changed, 113 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/apps/admin/views.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/apps/admin/views.py	Sat Apr 23 21:50:38 2011 +0200
@@ -34,7 +34,7 @@
     """
     User Account Browser
     """
-    if not flaskg.user.may.superuser(u''):
+    if not flaskg.user.may.superuser():
         abort(403)
 
     groups = flaskg.groups
@@ -56,7 +56,7 @@
     """
     Set values in user profile
     """
-    if not flaskg.user.may.superuser(u''):
+    if not flaskg.user.may.superuser():
         abort(403)
 
     uid = user.getUserId(user_name)
@@ -100,7 +100,7 @@
 
 @admin.route('/sysitems_upgrade', methods=['GET', 'POST', ])
 def sysitems_upgrade():
-    if not flaskg.user.may.superuser(u''):
+    if not flaskg.user.may.superuser():
         abort(403)
 
     from MoinMoin.storage.backends import upgrade_sysitems
@@ -125,7 +125,7 @@
 
 @admin.route('/wikiconfig', methods=['GET', ])
 def wikiconfig():
-    if not flaskg.user.may.superuser(u''):
+    if not flaskg.user.may.superuser():
         abort(403)
 
     settings = {}
@@ -172,7 +172,7 @@
 
 @admin.route('/wikiconfighelp', methods=['GET', ])
 def wikiconfighelp():
-    if not flaskg.user.may.superuser(u''):
+    if not flaskg.user.may.superuser():
         abort(403)
 
     def format_default(default):
--- a/MoinMoin/config/__init__.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/config/__init__.py	Sat Apr 23 21:50:38 2011 +0200
@@ -63,12 +63,16 @@
 # ACL rights that are valid in moin2
 SUPERUSER = 'superuser'
 NOTEXTCHA = 'notextcha'
+# rights that control access to specific functionality
+ACL_RIGHTS_FUNCTIONS = [SUPERUSER, NOTEXTCHA, ]
+
 ADMIN = 'admin'
 READ = 'read'
 WRITE = 'write'
 CREATE = 'create'
 DESTROY = 'destroy'
-ACL_RIGHTS_VALID = [READ, WRITE, CREATE, ADMIN, DESTROY, SUPERUSER, NOTEXTCHA, ]
+# rights that control access to operations on contents
+ACL_RIGHTS_CONTENTS = [READ, WRITE, CREATE, ADMIN, DESTROY, ]
 
 # metadata keys
 UUID = "uuid"
--- a/MoinMoin/config/default.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/config/default.py	Sat Apr 23 21:50:38 2011 +0200
@@ -23,6 +23,7 @@
 from MoinMoin import datastruct
 from MoinMoin.auth import MoinAuth
 from MoinMoin.util import plugins
+from MoinMoin.security import FunctionACL
 
 
 class CacheClass(object):
@@ -69,6 +70,9 @@
         self.cache.item_dict_regexact = re.compile(u'^%s$' % self.item_dict_regex, re.UNICODE)
         self.cache.item_group_regexact = re.compile(u'^%s$' % self.item_group_regex, re.UNICODE)
 
+        # compiled functions ACL
+        self.cache.acl_functions = FunctionACL(self, [self.acl_functions])
+
         plugins._loadPluginModule(self)
 
         if self.user_defaults['timezone'] is None:
@@ -189,7 +193,7 @@
             'interwiki_preferred',
             'item_root', 'item_license', 'mail_from',
             'item_dict_regex', 'item_group_regex',
-            'supplementation_item_names', 'html_pagetitle',
+            'acl_functions', 'supplementation_item_names', 'html_pagetitle',
             'theme_default', 'timezone_default', 'locale_default',
         )
 
@@ -301,7 +305,6 @@
 
     ('password_checker', DefaultExpression('_default_password_checker'),
      'checks whether a password is acceptable (default check is length >= 6, at least 4 different chars, no keyboard sequence, not username used somehow (you can switch this off by using `None`)'),
-
   )),
   # ==========================================================================
   'spam_leech_dos': ('Anti-Spam/Leech/DOS',
@@ -507,10 +510,14 @@
 #
 options = {
     'acl': ('Access control lists',
-    'ACLs control who may do what, see HelpOnAccessControlLists.',
+    'ACLs control who may do what.',
     (
-      ('rights_valid', config.ACL_RIGHTS_VALID,
-       "Valid tokens for right sides of ACL entries."),
+      ('functions', u'',
+       'Access Control List for functions.'),
+      ('rights_contents', config.ACL_RIGHTS_CONTENTS,
+       'Valid tokens for right sides of content ACL entries.'),
+      ('rights_functions', config.ACL_RIGHTS_FUNCTIONS,
+       'Valid tokens for right sides of function ACL entries.'),
     )),
 
     'ns': ('Storage Namespaces',
--- a/MoinMoin/datastruct/backends/_tests/__init__.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/datastruct/backends/_tests/__init__.py	Sat Apr 23 21:50:38 2011 +0200
@@ -14,7 +14,7 @@
 from flask import current_app as app
 from flask import g as flaskg
 
-from MoinMoin import security
+from MoinMoin.security import ContentACL
 from MoinMoin.datastruct import GroupDoesNotExistError
 
 
@@ -91,7 +91,7 @@
         Check user which has rights.
         """
         acl_rights = ["AdminGroup:admin,read,write"]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         for user in self.expanded_groups['AdminGroup']:
             for permission in ["read", "write", "admin"]:
@@ -103,7 +103,7 @@
         Check user which does not have rights.
         """
         acl_rights = ["AdminGroup:read,write"]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         assert u"SomeUser" not in flaskg.groups['AdminGroup']
         for permission in ["read", "write"]:
@@ -114,7 +114,7 @@
 
     def test_backend_acl_with_all(self):
         acl_rights = ["EditorGroup:read,write,admin All:read"]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         for member in self.expanded_groups[u'EditorGroup']:
             for permission in ["read", "write", "admin"]:
@@ -128,7 +128,7 @@
         assert u'NotExistingGroup' not in flaskg.groups
 
         acl_rights = ["NotExistingGroup:read,write,admin All:read"]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         assert not acl.may(u"Someone", "write")
 
--- a/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/datastruct/backends/_tests/test_wiki_groups.py	Sat Apr 23 21:50:38 2011 +0200
@@ -18,7 +18,7 @@
 from MoinMoin.datastruct.backends._tests import GroupsBackendTest
 from MoinMoin.datastruct import GroupDoesNotExistError
 from MoinMoin.config import USERGROUP
-from MoinMoin import security
+from MoinMoin.security import ContentACL
 from MoinMoin.user import User
 from MoinMoin._tests import become_trusted, create_random_string_list, update_item
 
@@ -110,7 +110,7 @@
         update_item(u'NewGroup', 0, {USERGROUP: ["ExampleUser"]}, DATA)
 
         acl_rights = ["NewGroup:read,write"]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         has_rights_before = acl.may(u"AnotherUser", "read")
 
--- a/MoinMoin/security/__init__.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/security/__init__.py	Sat Apr 23 21:50:38 2011 +0200
@@ -50,20 +50,23 @@
         self.name = user.name
 
     def __getattr__(self, attr):
-        """ Shortcut to export getPermission function for all known ACL rights
+        """ Shortcut to handle all known ACL rights.
 
-        if attr is one of the rights in acl_rights_valid, then return a
-        checking function for it. Else raise an AttributeError.
+        if attr is a valid acl right, return a checking function for it.
+        Else raise an AttributeError.
 
-        :param attr: one of ACL rights as defined in acl_rights_valid
+        :param attr: one of ACL rights as defined in acl_rights_(contents|functions)
         :rtype: function
-        :returns: checking function for that right, accepting an itemname
+        :returns: checking function for that right
         """
-        if attr not in app.cfg.acl_rights_valid:
-            raise AttributeError(attr)
-        ns_content = app.cfg.ns_content
-        may = flaskg.storage.get_backend(ns_content)._may
-        return lambda itemname: may(itemname, attr)
+        if attr in app.cfg.acl_rights_contents:
+            ns_content = app.cfg.ns_content # XXX always uses content backend
+            may = flaskg.storage.get_backend(ns_content)._may
+            return lambda itemname: may(itemname, attr) # XXX does not use self.name XXX
+        if attr in app.cfg.acl_rights_functions:
+            may = app.cfg.cache.acl_functions.may
+            return lambda: may(self.name, attr)
+        raise AttributeError(attr)
 
 
 # make an alias for the default policy
@@ -72,9 +75,7 @@
 
 class AccessControlList(object):
     """
-    Access Control List
-
-    Control who may do what on or with a wiki item.
+    Access Control List - controls who may do what.
 
     Syntax of an ACL string:
 
@@ -94,7 +95,7 @@
         "All" is a special group containing all users (Known and Anonymous users).
 
         "right" may be an arbitrary word like read, write or admin.
-        Only words in cfg.acl_rights_valid are accepted, others are ignored.
+        Only valid words are accepted, others are ignored (see valid param).
         It is allowed to specify no rights, which means that no rights are given.
 
     How ACL is processed
@@ -149,7 +150,7 @@
        For each backend in the namespace, you can configure the following
        ACL presets:
 
-        default acls:
+       default acls:
            These are ONLY used when no item ACLs are found.
            Default: "Known:read,write,create All:read,write",
 
@@ -160,21 +161,14 @@
        after acls:
            This will be inserted AFTER any item/default ACL entries.
            Default: ""
-
-       cfg.acl_rights_valid
-           These are the acceptable (known) rights (and the place to
-           extend, if necessary).
-           Default: ["read", "write", "create", "destroy", "admin"]
     """
 
     special_users = ["All", "Known", "Trusted"] # order is important
 
     def __init__(self, cfg, lines=[], default='', valid=None):
         """ Initialize an ACL, starting from <nothing>. """
-        if valid is None:
-            self.acl_rights_valid = cfg.acl_rights_valid
-        else:
-            self.acl_rights_valid = valid
+        assert valid is not None
+        self.acl_rights_valid = valid
         self.default = default
         self.auth_methods_trusted = cfg.auth_methods_trusted
         assert isinstance(lines, (list, tuple))
@@ -284,6 +278,30 @@
         return self.acl_lines != other.acl_lines
 
 
+class ContentACL(AccessControlList):
+    """
+    Content AccessControlList
+
+    Uses cfg.acl_rights_contents if no list of valid rights is explicitly given.
+    """
+    def __init__(self, cfg, lines=[], default='', valid=None):
+        if valid is None:
+            valid = cfg.acl_rights_contents
+        super(ContentACL, self).__init__(cfg, lines, default, valid)
+
+
+class FunctionACL(AccessControlList):
+    """
+    Function AccessControlList
+
+    Uses cfg.acl_rights_functions if no list of valid rights is explicitly given.
+    """
+    def __init__(self, cfg, lines=[], default='', valid=None):
+        if valid is None:
+            valid = cfg.acl_rights_functions
+        super(FunctionACL, self).__init__(cfg, lines, default, valid)
+
+
 class ACLStringIterator(object):
     """ Iterator for acl string
 
@@ -292,7 +310,7 @@
 
     Usage::
 
-        iter = ACLStringIterator(cfg.acl_rights_valid, 'user name:right')
+        iter = ACLStringIterator(rights_valid, 'user name:right')
         for modifier, entries, rights in iter:
             # process data
     """
--- a/MoinMoin/security/_tests/test_security.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/security/_tests/test_security.py	Sat Apr 23 21:50:38 2011 +0200
@@ -13,9 +13,7 @@
 from flask import current_app as app
 from flask import g as flaskg
 
-from MoinMoin import security
-acliter = security.ACLStringIterator
-AccessControlList = security.AccessControlList
+from MoinMoin.security import ContentACL, ACLStringIterator
 
 from MoinMoin.user import User
 from MoinMoin.config import ACL
@@ -23,56 +21,64 @@
 from MoinMoin._tests import update_item
 from MoinMoin._tests import become_trusted
 
+
+def acliter(acl):
+    """
+    return a acl string iterator (using cfg.acl_rights_contents as valid acl rights)
+    """
+    return ACLStringIterator(app.cfg.acl_rights_contents, acl)
+
+
 class TestACLStringIterator(object):
 
     def testEmpty(self):
         """ security: empty acl string raise StopIteration """
-        acl_iter = acliter(app.cfg.acl_rights_valid, '')
+        acl_iter = acliter('')
         py.test.raises(StopIteration, acl_iter.next)
 
     def testWhiteSpace(self):
         """ security: white space acl string raise StopIteration """
-        acl_iter = acliter(app.cfg.acl_rights_valid, '       ')
+        acl_iter = acliter('       ')
         py.test.raises(StopIteration, acl_iter.next)
 
     def testDefault(self):
         """ security: default meta acl """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'Default Default')
+        acl_iter = acliter('Default Default')
         for mod, entries, rights in acl_iter:
             assert entries == ['Default']
             assert rights == []
 
     def testEmptyRights(self):
         """ security: empty rights """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'WikiName:')
+        acl_iter = acliter('WikiName:')
         mod, entries, rights = acl_iter.next()
         assert entries == ['WikiName']
         assert rights == []
 
     def testSingleWikiNameSingleRight(self):
         """ security: single wiki name, single right """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'WikiName:read')
+        acl_iter = acliter('WikiName:read')
         mod, entries, rights = acl_iter.next()
         assert entries == ['WikiName']
         assert rights == ['read']
 
     def testMultipleWikiNameAndRights(self):
         """ security: multiple wiki names and rights """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne,UserTwo:read,write')
+        acl_iter = acliter('UserOne,UserTwo:read,write')
         mod, entries, rights = acl_iter.next()
         assert entries == ['UserOne', 'UserTwo']
         assert rights == ['read', 'write']
 
     def testMultipleWikiNameAndRightsSpaces(self):
         """ security: multiple names with spaces """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'user one,user two:read')
+        acl_iter = acliter('user one,user two:read')
         mod, entries, rights = acl_iter.next()
         assert entries == ['user one', 'user two']
         assert rights == ['read']
 
     def testMultipleEntries(self):
         """ security: multiple entries """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne:read,write UserTwo:read All:')
+        acl_iter = acliter('UserOne:read,write UserTwo:read All:')
         mod, entries, rights = acl_iter.next()
         assert entries == ['UserOne']
         assert rights == ['read', 'write']
@@ -85,14 +91,14 @@
 
     def testNameWithSpaces(self):
         """ security: single name with spaces """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'user one:read')
+        acl_iter = acliter('user one:read')
         mod, entries, rights = acl_iter.next()
         assert entries == ['user one']
         assert rights == ['read']
 
     def testMultipleEntriesWithSpaces(self):
         """ security: multiple entries with spaces """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'user one:read,write user two:read')
+        acl_iter = acliter('user one:read,write user two:read')
         mod, entries, rights = acl_iter.next()
         assert entries == ['user one']
         assert rights == ['read', 'write']
@@ -102,7 +108,7 @@
 
     def testMixedNames(self):
         """ security: mixed wiki names and names with spaces """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne,user two:read,write user three,UserFour:read')
+        acl_iter = acliter('UserOne,user two:read,write user three,UserFour:read')
         mod, entries, rights = acl_iter.next()
         assert entries == ['UserOne', 'user two']
         assert rights == ['read', 'write']
@@ -112,7 +118,7 @@
 
     def testModifier(self):
         """ security: acl modifiers """
-        acl_iter = acliter(app.cfg.acl_rights_valid, '+UserOne:read -UserTwo:')
+        acl_iter = acliter('+UserOne:read -UserTwo:')
         mod, entries, rights = acl_iter.next()
         assert mod == '+'
         assert entries == ['UserOne']
@@ -128,7 +134,7 @@
         The last part of this acl can not be parsed. If it ends with :
         then it will be parsed as one name with spaces.
         """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne:read user two is ignored')
+        acl_iter = acliter('UserOne:read user two is ignored')
         mod, entries, rights = acl_iter.next()
         assert entries == ['UserOne']
         assert rights == ['read']
@@ -140,7 +146,7 @@
         The documents does not talk about this case, may() should ignore
         the rights because there is no entry.
         """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne:read :read All:')
+        acl_iter = acliter('UserOne:read :read All:')
         mod, entries, rights = acl_iter.next()
         assert entries == ['UserOne']
         assert rights == ['read']
@@ -152,18 +158,17 @@
         assert rights == []
 
     def testIgnoreInvalidRights(self):
-        """ security: ignore rights not in acl_rights_valid
+        """ security: ignore rights not in acl_rights_contents
 
         Note: this is also important for ACL regeneration (see also acl
               regeneration test for storage.backends.fs19).
         """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne:read,sing,write,drink,sleep')
+        acl_iter = acliter('UserOne:read,sing,write,drink,sleep')
         mod, entries, rights = acl_iter.next()
         assert rights == ['read', 'write']
 
         # we use strange usernames that include invalid rights as substrings
-        acls = list(acliter(app.cfg.acl_rights_valid,
-                    u"JimAdelete,JoeArevert:admin,read,delete,write,revert"))
+        acls = list(acliter(u"JimAdelete,JoeArevert:admin,read,delete,write,revert"))
         # now check that we have lost the invalid rights 'delete' and 'revert',
         # but the usernames should be still intact.
         assert acls == [('', [u'JimAdelete', u'JoeArevert'], ['admin', 'read', 'write', ])]
@@ -173,7 +178,7 @@
 
         This test was failing on the apply acl rights test.
         """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne:read,write BadGuy: All:read')
+        acl_iter = acliter('UserOne:read,write BadGuy: All:read')
         mod, entries, rights = acl_iter.next()
         mod, entries, rights = acl_iter.next()
         assert entries == ['BadGuy']
@@ -181,7 +186,7 @@
 
     def testAllowExtraWhitespace(self):
         """ security: allow extra white space between entries """
-        acl_iter = acliter(app.cfg.acl_rights_valid, 'UserOne,user two:read,write   user three,UserFour:read  All:')
+        acl_iter = acliter('UserOne,user two:read,write   user three,UserFour:read  All:')
         mod, entries, rights = acl_iter.next()
         assert  entries == ['UserOne', 'user two']
         assert rights == ['read', 'write']
@@ -222,7 +227,7 @@
             "BadGuy:  "
             "All:read  "
             ]
-        acl = security.AccessControlList(app.cfg, acl_rights)
+        acl = ContentACL(app.cfg, acl_rights)
 
         # Should apply these rights:
         users = (
@@ -251,7 +256,7 @@
 
         # Check rights
         for user, may in users:
-            mayNot = [right for right in app.cfg.acl_rights_valid
+            mayNot = [right for right in app.cfg.acl_rights_contents
                       if right not in may]
             # User should have these rights...
             for right in may:
@@ -338,7 +343,7 @@
                 assert not can_access, "%r may not %s %r (normal)" % (u.name, right, itemname)
 
             # User should NOT have these rights:
-            mayNot = [right for right in app.cfg.acl_rights_valid
+            mayNot = [right for right in app.cfg.acl_rights_contents
                       if right not in may]
             for right in mayNot:
                 yield _not_have_right, u, right, itemname
@@ -423,7 +428,7 @@
                 assert not can_access, "%r may not %s %r (hierarchic)" % (u.name, right, itemname)
 
             # User should NOT have these rights:
-            mayNot = [right for right in app.cfg.acl_rights_valid
+            mayNot = [right for right in app.cfg.acl_rights_contents
                       if right not in may]
             for right in mayNot:
                 yield _not_have_right, u, right, itemname
--- a/MoinMoin/security/textcha.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/security/textcha.py	Sat Apr 23 21:50:38 2011 +0200
@@ -66,7 +66,7 @@
         user = flaskg.user
         textchas = cfg.textchas
 
-        if textchas and not user.may.notextcha(u''):
+        if textchas and not user.may.notextcha():
             locales = [user.locale, cfg.locale_default, 'en', ]
             for locale in locales:
                 logging.debug(u"TextCha: trying locale == '%s'." % locale)
--- a/MoinMoin/storage/_tests/test_backends_fs19.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/storage/_tests/test_backends_fs19.py	Sat Apr 23 21:50:38 2011 +0200
@@ -215,7 +215,7 @@
             (u'Joe Doe,Jane Doe:delete,read,write All:',
              u'Joe Doe,Jane Doe:read,write All:'), # multiple entries, blanks in names, remove 'delete'
         ]
-        acl_rights_valid = app.cfg.acl_rights_valid
+        acl_rights_valid = app.cfg.acl_rights_contents
         for acl, expected in tests:
             result = regenerate_acl(acl, acl_rights_valid)
             assert result == expected
--- a/MoinMoin/storage/backends/acl.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/storage/backends/acl.py	Sat Apr 23 21:50:38 2011 +0200
@@ -47,7 +47,7 @@
 from flask import current_app as app
 from flask import g as flaskg
 
-from MoinMoin.security import AccessControlList
+from MoinMoin.security import ContentACL
 
 from MoinMoin.storage import Item, NewRevision, StoredRevision
 from MoinMoin.storage.error import NoSuchItemError, NoSuchRevisionError, AccessDeniedError
@@ -86,9 +86,9 @@
         self.backend = backend
         self.hierarchic = hierarchic
         self.valid = valid
-        self.before = AccessControlList(cfg, [before], default=default, valid=valid)
-        self.default = AccessControlList(cfg, [default], default=default, valid=valid)
-        self.after = AccessControlList(cfg, [after], default=default, valid=valid)
+        self.before = ContentACL(cfg, [before], default=default, valid=valid)
+        self.default = ContentACL(cfg, [default], default=default, valid=valid)
+        self.after = ContentACL(cfg, [after], default=default, valid=valid)
 
     def __getattr__(self, attr):
         # Attributes that this backend does not define itself are just looked
@@ -173,7 +173,7 @@
         if not isinstance(acls, (tuple, list)):
             acls = (acls, )
         default = self.default.default
-        return AccessControlList(self.cfg, acls, default=default, valid=self.valid)
+        return ContentACL(self.cfg, acls, default=default, valid=self.valid)
 
     def _may(self, itemname, right):
         """ Check if self.username may have <right> access on item <itemname>.
@@ -198,7 +198,7 @@
         :rtype: bool
         :returns: True if you have permission or False
         """
-        username = flaskg.user.name
+        username = flaskg.user.name # XXX this is likely too inflexible / wrong
 
         allowed = self.before.may(username, right)
         if allowed is not None:
--- a/MoinMoin/storage/backends/fs19.py	Fri Apr 22 22:57:25 2011 +0200
+++ b/MoinMoin/storage/backends/fs19.py	Sat Apr 23 21:50:38 2011 +0200
@@ -395,8 +395,7 @@
 
         acl_line = self._fs_meta.get(ACL)
         if acl_line is not None:
-            self._fs_meta[ACL] = regenerate_acl(acl_line, config.ACL_RIGHTS_VALID)
-
+            self._fs_meta[ACL] = regenerate_acl(acl_line, config.ACL_RIGHTS_CONTENTS)
 
     def _process_data(self, meta, data):
         """ In moin 1.x markup, not all metadata is stored in the page's header.