def testApplyACLByGroup(self): """ security: applying acl by group name""" # This acl string... acl_rights = [ "PGroup,AllGroup:read,write,admin " "AGroup:read " ] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) # Should apply these rights: users = ( # user, rights ('Antony', ('read', 'write', 'admin', )), # in PGroup ('Beatrice', ('read', 'write', 'admin', )), # in PGroup ('Charles', ('read', )), # virtually in AGroup ) # Check rights for user, may in users: mayNot = [right for right in app.cfg.acl_rights_contents if right not in may] # User should have these rights... for right in may: assert acl.may(user, right) # But NOT these: for right in mayNot: assert not acl.may(user, right)
def testApplyACLByGroup(self): """ security: applying acl by group name""" # This acl string... acl_rights = [ "PGroup,AllGroup:read,write,admin " "AGroup:read " ] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) # Should apply these rights: users = ( # user, rights ('Antony', ('read', 'write', 'admin', )), # in PGroup ('Beatrice', ('read', 'write', 'admin', )), # in PGroup ('Charles', ('read', )), # virtually in AGroup ) # Check rights for user, may in users: mayNot = [right for right in app.cfg.acl_rights_contents if right not in may] # User should have these rights... for right in may: assert acl.may(user, right) # But NOT these: for right in mayNot: assert not acl.may(user, right)
def test_backend_acl_with_all(self): acl_rights = ["EditorGroup:read,write,admin All:read"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) for member in self.expanded_groups[u'EditorGroup']: for permission in ["read", "write", "admin"]: assert acl.may(member, permission) assert acl.may(u"Someone", "read") for permission in ["write", "admin"]: assert not acl.may(u"Someone", permission)
def testApplyACLByUser(self): """ security: applying acl by user name""" # This acl string... acl_rights = [ "-MinusGuy:read " "+MinusGuy:read " "+PlusGuy:read " "-PlusGuy:read " "Admin1,Admin2:read,write,admin " "Admin3:read,write,admin " "JoeDoe:read,write " "name with spaces,another one:read,write " "CamelCase,extended name:read,write " "BadGuy: " "All:read " ] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) # Should apply these rights: users = ( # user, rights # CamelCase names ('Admin1', ('read', 'write', 'admin')), ('Admin2', ('read', 'write', 'admin')), ('Admin3', ('read', 'write', 'admin')), ('JoeDoe', ('read', 'write')), ('SomeGuy', ('read', )), # Extended names or mix of extended and CamelCase ('name with spaces', ('read', 'write', )), ('another one', ('read', 'write', )), ('CamelCase', ('read', 'write', )), ('extended name', ('read', 'write', )), # Blocking bad guys ('BadGuy', ()), # All other users - every one not mentioned in the acl lines ('All', ('read', )), ('Anonymous', ('read', )), # we check whether ACL processing stops for a user/right match # with ACL modifiers ('MinusGuy', ()), ('PlusGuy', ('read', )), ) # Check rights for user, may in users: mayNot = [right for right in app.cfg.acl_rights_contents if right not in may] # User should have these rights... for right in may: assert acl.may(user, right) # But NOT these: for right in mayNot: assert not acl.may(user, right)
def testApplyACLByUser(self): """ security: applying acl by user name""" # This acl string... acl_rights = [ "-MinusGuy:read " "+MinusGuy:read " "+PlusGuy:read " "-PlusGuy:read " "Admin1,Admin2:read,write,admin " "Admin3:read,write,admin " "JoeDoe:read,write " "name with spaces,another one:read,write " "CamelCase,extended name:read,write " "BadGuy: " "All:read " ] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) # Should apply these rights: users = ( # user, rights # CamelCase names ('Admin1', ('read', 'write', 'admin')), ('Admin2', ('read', 'write', 'admin')), ('Admin3', ('read', 'write', 'admin')), ('JoeDoe', ('read', 'write')), ('SomeGuy', ('read', )), # Extended names or mix of extended and CamelCase ('name with spaces', ('read', 'write', )), ('another one', ('read', 'write', )), ('CamelCase', ('read', 'write', )), ('extended name', ('read', 'write', )), # Blocking bad guys ('BadGuy', ()), # All other users - every one not mentioned in the acl lines ('All', ('read', )), ('Anonymous', ('read', )), # we check whether ACL processing stops for a user/right match # with ACL modifiers ('MinusGuy', ()), ('PlusGuy', ('read', )), ) # Check rights for user, may in users: mayNot = [right for right in app.cfg.acl_rights_contents if right not in may] # User should have these rights... for right in may: assert acl.may(user, right) # But NOT these: for right in mayNot: assert not acl.may(user, right)
def test_backend_acl_deny(self): """ Test if the wiki group backend works with acl code. Check user which does not have rights. """ acl_rights = ["AdminGroup:read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) assert u"SomeUser" not in flaskg.groups['AdminGroup'] for permission in ["read", "write"]: assert not acl.may(u"SomeUser", permission), 'SomeUser must not have {0} permission because he is not listed in the AdminGroup'.format(permission) assert u'Admin1' in flaskg.groups['AdminGroup'] assert not acl.may(u"Admin1", "admin")
def test_backend_acl_not_existing_group(self): assert u'NotExistingGroup' not in flaskg.groups acl_rights = ["NotExistingGroup:read,write,admin All:read"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) assert not acl.may(u"Someone", "write")
def test_backend_acl_allow(self): """ Test if the wiki group backend works with acl code. Check user which has rights. """ acl_rights = ["AdminGroup:admin,read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) for user in self.expanded_groups['AdminGroup']: for permission in ["read", "write", "admin"]: assert acl.may(u"Admin1", permission), '{0} must have {1} permission because he is member of the AdminGroup'.format(user, permission)
def test_wiki_backend_item_acl_usergroupmember_item(self): """ Test if the wiki group backend works with acl code. First check acl rights of a user that is not a member of group then add user member to an item group and check acl rights """ become_trusted() update_item(u'NewGroup', {USERGROUP: ["ExampleUser"]}, DATA) acl_rights = ["NewGroup:read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) has_rights_before = acl.may(u"AnotherUser", "read") # update item - add AnotherUser to a item group NewGroup update_item(u'NewGroup', {USERGROUP: ["AnotherUser"]}, '') has_rights_after = acl.may(u"AnotherUser", "read") assert not has_rights_before, 'AnotherUser has no read rights because in the beginning he is not a member of a group item NewGroup' assert has_rights_after, 'AnotherUser must have read rights because after appenditem he is member of NewGroup'
def test_wiki_backend_item_acl_usergroupmember_item(self): """ Test if the wiki group backend works with acl code. First check acl rights of a user that is not a member of group then add user member to an item group and check acl rights """ become_trusted() update_item(u'NewGroup', {USERGROUP: ["ExampleUser"]}, DATA) acl_rights = ["NewGroup:read,write"] acl = AccessControlList(acl_rights, valid=app.cfg.acl_rights_contents) has_rights_before = acl.may(u"AnotherUser", "read") # update item - add AnotherUser to a item group NewGroup update_item(u'NewGroup', {USERGROUP: ["AnotherUser"]}, '') has_rights_after = acl.may(u"AnotherUser", "read") assert not has_rights_before, 'AnotherUser has no read rights because in the beginning he is not a member of a group item NewGroup' assert has_rights_after, 'AnotherUser must have read rights because after appenditem he is member of NewGroup'
class AclWrapperBackend(object): """ The AMW is bound to a specific request. The actual backend is retrieved from the config upon request initialization. Any method that is in some way relevant to security needs to be wrapped in order to ensure the user has the permissions necessary to perform the desired action. Note: This may *not* inherit from MoinMoin.storage.Backend because that would break our __getattr__ attribute 'redirects' (which are necessary because a backend implementor may decide to use his own helper functions which the items and revisions will still try to call). """ def __init__(self, cfg, backend, hierarchic=False, before=u"", default=u"", after=u"", valid=None): """ @type backend: Some object that implements the storage API. @param backend: The unprotected backend that we want to protect. @type hierarchic: bool @param hierarchic: Indicate whether we want to process ACLs in hierarchic mode. @type before: unicode @param before: ACL to be applied before all the other ACLs. @type default: unicode @param default: If no ACL information is given on the item in question, use this default. @type after: unicode @param after: ACL to be applied after all the other ACLs. @type valid: list of strings or None @param valid: If a list is given, only strings in the list are treated as valid acl privilege descriptors. If None is give, the global wiki default is used. """ self.cfg = cfg 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) def __getattr__(self, attr): # Attributes that this backend does not define itself are just looked # up on the real backend. return getattr(self.backend, attr) def search_items(self, searchterm): """ @see: Backend.search_items.__doc__ """ for item in self.backend.search_items(searchterm): if self._may(item.name, READ): # The item returned needs to be wrapped because otherwise the # item's methods (like create_revision) wouldn't be wrapped. wrapped_item = AclWrapperItem(item, self) yield wrapped_item def get_item(self, itemname): """ @see: Backend.get_item.__doc__ """ if not self._may(itemname, READ): raise AccessDeniedError(flaskg.user.name, READ, itemname) real_item = self.backend.get_item(itemname) # Wrap the item here as well. wrapped_item = AclWrapperItem(real_item, self) return wrapped_item def has_item(self, itemname): """ @see: Backend.has_item.__doc__ """ # We do not hide the sheer existance of items. When trying # to create an item with the same name, the user would notice anyway. return self.backend.has_item(itemname) def create_item(self, itemname): """ @see: Backend.create_item.__doc__ """ if not self._may(itemname, CREATE): raise AccessDeniedError(flaskg.user.name, CREATE, itemname) real_item = self.backend.create_item(itemname) # Wrap item. wrapped_item = AclWrapperItem(real_item, self) return wrapped_item def iteritems(self): """ @see: Backend.iteritems.__doc__ """ for item in self.backend.iteritems(): if self._may(item.name, READ): yield AclWrapperItem(item, self) def history(self, reverse=True): """ @see: Backend.history.__doc__ """ for revision in self.backend.history(reverse): if self._may(revision.item.name, READ): # The revisions returned here should only be StoredRevisions. # We wrap them nevertheless to be sure. Esp. revision.item # would otherwise give access to an unwrapped item. item = revision.item item = AclWrapperItem(item, self) revision = AclWrapperRevision(revision, item) yield revision def _get_acl(self, itemname): """ Get ACL strings from the last revision's metadata and return ACL object. """ try: item = self.backend.get_item(itemname) # we always use the ACLs set on the latest revision: current_rev = item.get_revision(-1) acls = current_rev[ACL] except (NoSuchItemError, NoSuchRevisionError, KeyError): # do not use default acl here acls = [] if not isinstance(acls, (tuple, list)): acls = (acls, ) default = self.default.default return AccessControlList(self.cfg, acls, default=default, valid=self.valid) def _may(self, itemname, right): """ Check if self.username may have <right> access on item <itemname>. For hierarchic=False we just check the item in question. For hierarchic=True, we check each item in the hierarchy. We start with the deepest item and recurse to the top of the tree. If one of those permits, True is returned. This is done *only* if there is *no ACL at all* (not even an empty one) on the items we 'recurse over'. For both configurations, we check `before` before the item/default acl and `after` after the item/default acl, of course. `default` is only used if there is no ACL on the item (and none on any of the item's parents when using hierarchic.) @param itemname: item to get permissions from @param right: the right to check @rtype: bool @return: True if you have permission or False """ username = flaskg.user.name allowed = self.before.may(username, right) if allowed is not None: return allowed if self.hierarchic: items = itemname.split('/') # create item hierarchy list some_acl = False for i in range(len(items), 0, -1): # Create the next pagename in the hierarchy # starting at the leaf, going to the root name = '/'.join(items[:i]) acl = self._get_acl(name) if acl.has_acl(): some_acl = True allowed = acl.may(username, right) if allowed is not None: return allowed # If the item has an acl (even one that doesn't match) we *do not* # check the parents. We only check the parents if there's no acl on # the item at all. break if not some_acl: allowed = self.default.may(username, right) if allowed is not None: return allowed else: acl = self._get_acl(itemname) if acl.has_acl(): allowed = acl.may(username, right) if allowed is not None: return allowed else: allowed = self.default.may(username, right) if allowed is not None: return allowed allowed = self.after.may(username, right) if allowed is not None: return allowed return False