changeset 4768:235bb30c9bd6

Groups 2009: merge with the 1.9 branch
author Dmitrijs Milajevs <dimazest@gmail.com>
date Tue, 26 May 2009 16:48:21 +0200
parents 3b34bbd60dca (diff) 70c8457d68ab (current diff)
children 55c40ed9dac1
files
diffstat 4 files changed, 358 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/groups/__init__.py	Tue May 26 16:48:21 2009 +0200
@@ -0,0 +1,140 @@
+# -*- coding: iso-8859-1 -*-
+
+"""
+MoinMoin - group definition access via various backends.
+
+TODO Group name mapping for the BackendManager.
+
+@copyright: 2009 DmitrijsMilajevs
+@license: GPL, see COPYING for details
+"""
+
+
+class BackendManager(object):
+    """
+    BackendManager maps string to the Group object. String represents
+    group name. It provides access to groups of specific backend.
+    """
+
+    def __init__(self, backend, mapper_to_backend=None, mapper_from_backend=None):
+        """
+        Creates backend manager object.
+
+        XXX Decorators can be used for group name mapping.
+
+        @type backend: group backend object.
+        @param backend: the backend which provides access to the group
+        definitions.
+
+        @type mapper_to_backend: function which takes one string as an
+        argument and returns a string
+        @param mapper_to_backend: function which maps moin group
+        name to the backend group name
+
+        @type mapper_from_backend: function which takes one string as an
+        argument and returns a string
+        @param mapper_from_backend: function which maps backend group
+        name to the moin group name
+        """
+        self._backend = backend
+
+        # XXX Should we check that *two* mapper functions are passed,
+        # and if not throw an exception?
+        if mapper_to_backend is not None:
+            self.mapper_to_backend = mapper_to_backend
+        else:
+            # Nothing to map, just return unmodified string
+            self.mapper_to_backend = lambda x: x
+
+        if mapper_from_backend is not None:
+            self.mapper_from_backend = mapper_from_backend
+        else:
+            # Nothing to map, just return unmodified string
+            self.mapper_from_backend = lambda x: x
+
+    def __getitem__(self, group_name):
+        """
+        Selection of a group by its name.
+
+        @type group_name: unicode string.
+        @param group_name: name of the group which object to select.
+        """
+        return self._backend[self.mapper_to_backend(group_name)]
+
+    def __iter__(self):
+        """
+        Iteration over group names.
+        """
+        return (self.mapper_from_backend(group_name) for group_name in self._backend)
+
+    def __contains__(self, group_name):
+        """
+        Check if a group called group name is avaliable via this backend.
+
+        @type group_name: unicode string.
+        @param group_name: name of the group which is checked for an containment.
+        """
+        return self.mapper_to_backend(group_name) in self._backend
+
+    def membergroups(self, member):
+        """
+        List all groups where member is a member of.
+        @rtype: list of unicode strings
+        @return: list of group names in which member takes part in
+        """
+        return [group_name for group_name in self
+                if member in self[group_name]]
+
+
+class GroupManager(object):
+    """
+    GroupManager manages several group backends.
+    """
+
+    def __init__(self, backends):
+        """
+        Create a group manager object.
+
+        @type backends: list of objects.
+        @param backend: group backends which are used to get access to the
+        group definitions.
+        """
+        self._backends = backends
+
+    def __getitem__(self, group_name):
+        """
+        Selection of a group by its name.
+        """
+        for backend in self._backends:
+            if group_name in backend:
+                return backend[group_name]
+        raise KeyError("There is no such group")
+
+    def __iter__(self):
+        """
+        Iteration over groups names.
+        """
+        yielded_groups = set()
+
+        for backend in self._backends:
+            for group_name in backend:
+                if group_name not in yielded_groups:
+                    yield group_name
+                    yielded_groups.add(group_name)
+
+    def __contains__(self, group_name):
+        """
+        Check if a group called group_name is defined.
+        """
+        for backend in self._backends:
+            if group_name in backend:
+                return True
+
+    def membergroups(self, member):
+        """
+        List all groups where member is a member of.
+        @rtype: list of unicode strings
+        @return: list of group names in which member takes part in
+        """
+        return [group_name for group_name in self
+                         if member in self[group_name]]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/groups/_test/test_backend_manager.py	Tue May 26 16:48:21 2009 +0200
@@ -0,0 +1,109 @@
+# -*- coding: iso-8859-1 -*-
+
+"""
+MoinMoin.groups.BackendManager test
+
+@copyright: 2009 MoinMoin:DmitrijsMilajevs
+@license: GPL, see COPYING for details
+"""
+
+from py.test import raises
+
+from MoinMoin.groups import BackendManager
+
+
+class TestBackendManagerAPI(object):
+    """
+    This tastcase test Backend manager API
+    """
+
+    def setup_method(self, method):
+        self.admin_group = frozenset([u'Admin', u'JohnDoe'])
+        self.editor_group = frozenset([u'MainEditor', u'JohnDoe'])
+        self.fruit_group = frozenset([u'Apple', u'Banana'])
+
+        groups = {u'AdminGroup': self.admin_group,
+                  u'EditorGroup': self.editor_group,
+                  u'FruitGroup': self.fruit_group}
+
+        self.group_backend = BackendManager(backend=groups)
+
+    def test_getitem(self):
+        """
+        Test of the __getitem__ API method. It should return a group
+        object by the group name.
+        """
+        assert self.admin_group == self.group_backend[u'AdminGroup']
+        assert self.fruit_group == self.group_backend[u'FruitGroup']
+
+        raises(KeyError, lambda: self.group_backend[u'not existing group'])
+
+    def test_contains(self):
+        """
+        Test of the __contains__ API method. It checks if a group is
+        avaliable via this backend. Check is done by group's name.
+        """
+        assert u'AdminGroup' in self.group_backend
+        assert u'FruitGroup' in self.group_backend
+
+        assert u'not existing group' not in self.group_backend
+
+    def  test_membergroups(self):
+        """
+        Test of membergroups API method. It lists all groups where
+        member is a member of. It should  return a list of group names.
+        """
+        apple_groups = self.group_backend.membergroups(u'Apple')
+        assert 1 == len(apple_groups)
+        assert u'FruitGroup' in apple_groups
+        assert u'AdminGroup' not in apple_groups
+
+        john_doe_groups = self.group_backend.membergroups(u'JohnDoe')
+        assert 2 == len(john_doe_groups)
+        assert u'EditorGroup' in john_doe_groups
+        assert u'AdminGroup' in john_doe_groups
+        assert u'FruitGroup' not in john_doe_groups
+
+
+class TestManagerMapping(object):
+    """
+    This class tests mapping of the group names from a backend to the
+    moin and from the moin to a backend.
+
+    Here the simplest situation is considered. Moin expect groups to
+    be named as *Group, but backend stores group names without this prefix.
+
+    When group names are passed or retrieved from the backend they
+    should be mapped.
+    """
+
+    def setup_class(self):
+        self.admin_group = frozenset([u'Admin', u'JohnDoe'])
+        self.editor_group = frozenset([u'MainEditor', u'JohnDoe'])
+
+        # Group names here do not follow moin convention: they do not
+        # have group prefix.
+        groups = {u'Admin': self.admin_group,
+                  u'Editor': self.editor_group}
+
+        # Simply drop last five letters, what is length of word "Group"
+        mapper_to_backend = lambda group_name: group_name[:-5]
+        # Add "Group" postfix for every group name received from a backend
+        mapper_from_backend = lambda group_name: "%sGroup" % group_name
+
+        self.group_backend = BackendManager(backend=groups,
+                                            mapper_to_backend=mapper_to_backend,
+                                            mapper_from_backend=mapper_from_backend)
+
+
+    def test_getitem(self):
+        admin_group = self.group_backend[u'AdminGroup']
+        assert self.admin_group == admin_group
+
+    def test_contains(self):
+        assert u'AdminGroup' in self.group_backend
+
+    def test_membersgroups(self):
+        assert u'AdminGroup' in self.group_backend.membergroups(u'JohnDoe')
+        
+coverage_modules = ['MoinMoin.groups']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/groups/_test/test_group_manager.py	Tue May 26 16:48:21 2009 +0200
@@ -0,0 +1,105 @@
+# -*- coding: iso-8859-1 -*-
+
+"""
+MoinMoin.groups.GroupManager test
+
+@copyright: 2009 MoinMoin:DmitrijsMilajevs
+@license: GPL, see COPYING for details
+"""
+
+from py.test import raises
+
+from MoinMoin.groups import BackendManager, GroupManager
+
+
+class TestGroupManagerAPI(object):
+    """
+    Performs test of the API of GroupManager.
+    """
+
+    def setup_method(self, method):
+        self.admin_group = frozenset([u'Admin', u'JohnDoe'])
+        self.editor_group = frozenset([u'MainEditor', u'JohnDoe'])
+        self.fruit_group = frozenset([u'Apple', u'Banana', u'Cherry'])
+
+        first_backend = BackendManager({u'AdminGroup': self.admin_group,
+                                        u'EditorGroup': self.editor_group,
+                                        u'FruitGroup': self.fruit_group})
+
+        self.user_group = frozenset([u'JohnDoe', u'Bob', u'Joe'])
+        self.city_group = frozenset([u'Bolzano', u'Riga', u'London'])
+        # Suppose, someone hacked second backend
+        # and added himself to AdminGroup
+        self.second_admin_group = frozenset([u'TheHacker'])
+
+        second_backend = BackendManager({u'UserGroup': self.user_group,
+                                         u'CityGroup': self.city_group,
+                                         # Here group name clash occurs.
+                                         # AdminGroup is defined in both
+                                         # first_backend and second_backend.
+                                         u'AdminGroup': self.second_admin_group})
+
+        self.group_manager = GroupManager(backends = [first_backend,
+                                                      second_backend])
+
+    def test_getitem(self):
+        """
+        Tests __getitem__ API method. It should return a group by its name.
+        """
+        assert self.fruit_group == self.group_manager[u'FruitGroup']
+        raises(KeyError, lambda: self.group_manager[u'not existing group'])
+
+    def test_clashed_getitem(self):
+        """
+        This test check situation when groups with a same name are
+        defined in several backends. In this case, the only one
+        backend must be taken in consideration, that backend which is
+        defined first in the backends list.
+        """
+        admin_group = self.group_manager[u'AdminGroup']
+
+        assert self.admin_group == admin_group
+
+        # Nevertheless, TheHacker added himself to the second backend,
+        # it must not be taken into consideration, because AdminGroup is defined
+        # in first backend
+        assert u'TheHacker' not in admin_group
+
+    def test_iter(self):
+        """
+        Tests __iter__ API method. It should iterate over all groups
+        available via backends. It should avoid group name clashes.
+        """
+        all_group_names = [group_name for group_name in self.group_manager]
+
+        assert 5 == len(all_group_names)
+        # There are no duplicates
+        assert len(set(all_group_names)) == len(all_group_names)
+
+    def test_contains(self):
+        """
+        Tests __contains__ API method. It should check if a group
+        called group_name is available via some backend.
+        """
+        assert u'UserGroup' in self.group_manager
+        assert u'not existing group' not in self.group_manager
+
+    def test_membergroups(self):
+        """
+        Tests membergroups API method. It should lists all groups
+        where member is a member of. It should return a list of group
+        names.
+        """
+        apple_groups = self.group_manager.membergroups(u'Apple')
+        assert 1 == len(apple_groups)
+        assert u'FruitGroup' in apple_groups
+        assert u'AdminGroup' not in apple_groups
+
+        john_doe_groups = self.group_manager.membergroups(u'JohnDoe')
+        assert 3 == len(john_doe_groups)
+        assert u'EditorGroup' in john_doe_groups
+        assert u'AdminGroup' in john_doe_groups
+        assert u'UserGroup' in john_doe_groups
+        assert u'FruitGroup' not in john_doe_groups
+
+coverage_modules = ['MoinMoin.groups']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/CHANGES.dmilajevs	Tue May 26 16:48:21 2009 +0200
@@ -0,0 +1,4 @@
+Version 1.9-groups-dmilajevs:
+
+   New features:
+   * Group backends. Group definitions can be stored outside of MoinMoin.
\ No newline at end of file