1. Ronny Pfannschmidt
  2. moin-2.0

Commits

Thomas Waldmann  committed cd8afd4

storage fixes / extensions, made ACLs work, updated MoinMoin.config

  • Participants
  • Parent commits fe6df58
  • Branches storage-ng

Comments (0)

Files changed (17)

File MoinMoin/config/__init__.py

View file
 ADMIN = 'admin'
 READ = 'read'
 WRITE = 'write'
+OVERWRITE = 'overwrite'
 CREATE = 'create'
 DESTROY = 'destroy'
 # rights that control access to operations on contents
 ACL_RIGHTS_CONTENTS = [READ, WRITE, CREATE, ADMIN, DESTROY, ]
 
 # metadata keys
-UUID = "uuid"
 NAME = "name"
 NAME_OLD = "name_old"
 
 
 # some field names for whoosh index schema / documents in index:
 NAME_EXACT = "name_exact"
-REV_NO = "rev_no"
+ITEMID = "itemid"
+REVID = "revid"
+DATAID = "dataid"
 WIKINAME = "wikiname"
 CONTENT = "content"
 
+# magic REVID for current revision:
+CURRENT = "current"
+
 # stuff from user profiles / for whoosh index
 EMAIL = "email"
 OPENID = "openid"

File MoinMoin/storage/__init__.py

View file
 
 CONTENT, USERPROFILES = 'content', 'userprofiles'
 
-BACKENDS_PACKAGE = 'storage.backends'
+BACKENDS_PACKAGE = 'MoinMoin.storage.backends'
 
 
 def backend_from_uri(uri):
     if len(backend_name_uri) != 2:
         raise ValueError("malformed backend uri: %s" % backend_uri)
     backend_name, backend_uri = backend_name_uri
-    module = __import__(BACKENDS_PACKAGE + '.' + backend_name, globals(), locals(), ['Backend', ])
-    return module.Backend.from_uri(backend_uri)
+    module = __import__(BACKENDS_PACKAGE + '.' + backend_name, globals(), locals(), ['MutableBackend', ])
+    return module.MutableBackend.from_uri(backend_uri)
 
 
-def create_mapping(uri, mounts_acls):
-    namespace_mapping = [(mounts_acls[nsname][0],
-                          backend_from_uri(uri % dict(nsname=nsname)),
-                          mounts_acls[nsname][1])
-                         for nsname in mounts_acls]
+def create_mapping(uri, mounts, acls):
+    namespace_mapping = [(mounts[nsname],
+                          backend_from_uri(uri % dict(nsname=nsname)))
+                         for nsname in mounts]
+    acl_mapping = acls.items()
     # we need the longest mountpoints first, shortest last (-> '' is very last)
-    return sorted(namespace_mapping, key=lambda x: len(x[0]), reverse=True)
-
+    namespace_mapping = sorted(namespace_mapping, key=lambda x: len(x[0]), reverse=True)
+    acl_mapping = sorted(acl_mapping, key=lambda x: len(x[0]), reverse=True)
+    return namespace_mapping, acl_mapping
 
 def create_simple_mapping(uri='stores:fs:instance',
                           content_acl=None, user_profile_acl=None):
         content_acl = dict(before=u'', default=u'All:read,write,create', after=u'', hierarchic=False)
     if not user_profile_acl:
         user_profile_acl = dict(before=u'All:', default=u'', after=u'', hierarchic=False)
-    mounts_acls = {
-        CONTENT: ('', content_acl),
-        USERPROFILES: ('UserProfile', user_profile_acl),
+    mounts = {
+        CONTENT: '',
+        USERPROFILES: 'UserProfile',
     }
-    return create_mapping(uri, mounts_acls)
+    acls = {
+        'UserProfile/': user_profile_acl,
+        '': content_acl,
+    }
+    return create_mapping(uri, mounts, acls)
 

File MoinMoin/storage/backends/_tests/test_stores.py

View file
 from ..stores import MutableBackend
 from . import MutableBackendTestBase
 
-from storage.stores.memory import BytesStore as MemoryBytesStore
-from storage.stores.memory import FileStore as MemoryFileStore
+from MoinMoin.storage.stores.memory import BytesStore as MemoryBytesStore
+from MoinMoin.storage.stores.memory import FileStore as MemoryFileStore
 
 class TestMemoryBackend(MutableBackendTestBase):
     def setup_method(self, method):
 import os
 import tempfile
 
-from storage.stores.fs import BytesStore as FSBytesStore
-from storage.stores.fs import FileStore as FSFileStore
+from MoinMoin.storage.stores.fs import BytesStore as FSBytesStore
+from MoinMoin.storage.stores.fs import FileStore as FSFileStore
 
 class TestFSBackend(MutableBackendTestBase):
     def setup_method(self, method):

File MoinMoin/storage/backends/fileserver.py

View file
 import stat
 from StringIO import StringIO
 
-from config import MTIME, SIZE, CONTENTTYPE
+from MoinMoin.config import MTIME, SIZE, CONTENTTYPE
 from . import BackendBase
 
 

File MoinMoin/storage/backends/stores.py

View file
 
 from __future__ import absolute_import, division
 
-from uuid import uuid4
-make_uuid = lambda: unicode(uuid4().hex)
-
-from config import REVID, DATAID, SIZE, HASH_ALGORITHM
+from MoinMoin.config import REVID, DATAID, SIZE, HASH_ALGORITHM
+from MoinMoin.util.crypto import make_uuid
 
 from . import BackendBase, MutableBackendBase
 from ._util import TrackingFileWrapper
 except ImportError:
     import simplejson as json
 
-STORES_PACKAGE = 'storage.stores'
+STORES_PACKAGE = 'MoinMoin.storage.stores'
 
 
 class Backend(BackendBase):
         module = __import__(STORES_PACKAGE + '.' + store_name, globals(), locals(), ['BytesStore', 'FileStore', ])
         meta_store_uri = store_uri % dict(kind='meta')
         data_store_uri = store_uri % dict(kind='data')
-        return cls(module.BytesStore(meta_store_uri), module.FileStore(data_store_uri))
+        return cls(module.BytesStore.from_uri(meta_store_uri), module.FileStore.from_uri(data_store_uri))
 
     def __init__(self, meta_store, data_store):
         """

File MoinMoin/storage/middleware/_tests/test_indexing.py

View file
 
 from ..indexing import IndexingMiddleware
 
-from storage.backends.stores import MutableBackend
-from storage.stores.memory import BytesStore as MemoryBytesStore
-from storage.stores.memory import FileStore as MemoryFileStore
+from MoinMoin.storage.backends.stores import MutableBackend
+from MoinMoin.storage.stores.memory import BytesStore as MemoryBytesStore
+from MoinMoin.storage.stores.memory import FileStore as MemoryFileStore
 
 
 class TestIndexingMiddleware(object):

File MoinMoin/storage/middleware/_tests/test_protecting.py

View file
 UNPROTECTED_CONTENT = 'unprotected content'
 PROTECTED_CONTENT = 'protected content'
 
+acl_mapping = [
+    ('', dict(before=u'', default=u'All:read,write,create', after=u'', hierarchic=False)),
+]
+
+class User(object):
+    """
+    fake user object, just to give user.name
+    """
+    def __init__(self, name):
+        self.name = name
+
 class TestProtectingMiddleware(TestIndexingMiddleware):
     def setup_method(self, method):
         super(TestProtectingMiddleware, self).setup_method(method)
-        self.imw = ProtectingMiddleware(self.imw, user_name=u'joe')
+        self.imw = ProtectingMiddleware(self.imw, User(u'joe'), acl_mapping=acl_mapping)
 
     def teardown_method(self, method):
         self.imw = self.imw.indexer
         revid_unprotected, revid_protected = self.make_items(u'joe:read', u'boss:read')
         revids = [rev.revid for rev in self.imw.documents(all_revs=False)]
         assert revids == [revid_unprotected]  # without revid_protected!
-    
+
     def test_getitem(self):
         revid_unprotected, revid_protected = self.make_items(u'joe:read', u'boss:read')
         # now testing:
         item = self.imw[item_name]
         item.store_revision(dict(name=item_name), StringIO('new content'))
 
-    def test_overwrite(self):
-        revid_unprotected, revid_protected = self.make_items(u'joe:write joe:overwrite', u'boss:write boss:overwrite')
+    def test_overwrite_revision(self):
+        revid_unprotected, revid_protected = self.make_items(u'joe:write,overwrite', u'boss:write,overwrite')
         # now testing:
         item = self.imw[UNPROTECTED]
-        item.store_revision(dict(name=UNPROTECTED, acl=u'joe:write joe:overwrite', revid=revid_unprotected),
+        item.store_revision(dict(name=UNPROTECTED, acl=u'joe:write,overwrite', revid=revid_unprotected),
                             StringIO(UNPROTECTED_CONTENT), overwrite=True)
         item = self.imw[PROTECTED]
         with pytest.raises(AccessDenied):
-            item.store_revision(dict(name=PROTECTED, acl=u'boss:write boss:overwrite', revid=revid_protected),
+            item.store_revision(dict(name=PROTECTED, acl=u'boss:write,overwrite', revid=revid_protected),
                                 StringIO(UNPROTECTED_CONTENT), overwrite=True)
 
-    def test_destroy(self):
+    def test_destroy_revision(self):
+        revid_unprotected, revid_protected = self.make_items(u'joe:destroy', u'boss:destroy')
+        # now testing:
+        item = self.imw[UNPROTECTED]
+        item.destroy_revision(revid_unprotected)
+        item = self.imw[PROTECTED]
+        with pytest.raises(AccessDenied):
+            item.destroy_revision(revid_protected)
+
+
+    def test_destroy_item(self):
         revid_unprotected, revid_protected = self.make_items(u'joe:destroy', u'boss:destroy')
         # now testing:
         item = self.imw[UNPROTECTED]
             item.destroy_all_revisions()
 
 
+

File MoinMoin/storage/middleware/_tests/test_routing.py

View file
 
 from ..routing import Backend as RouterBackend
 
-from storage.backends.stores import MutableBackend as StoreBackend, Backend as ROBackend
-from storage.stores.memory import BytesStore as MemoryBytesStore
-from storage.stores.memory import FileStore as MemoryFileStore
+from MoinMoin.storage.backends.stores import MutableBackend as StoreBackend, Backend as ROBackend
+from MoinMoin.storage.stores.memory import BytesStore as MemoryBytesStore
+from MoinMoin.storage.stores.memory import FileStore as MemoryFileStore
 
 
 def make_ro_backend():
     store = StoreBackend(MemoryBytesStore(), MemoryFileStore())
     store.create()
+    store.open()
     store.store({NAME: 'test'}, StringIO(''))
     store.store({NAME: 'test2'}, StringIO(''))
     return ROBackend(store.meta_store, store.data_store)
     sub_be = StoreBackend(MemoryBytesStore(), MemoryFileStore())
     ro_be = make_ro_backend()
     router = RouterBackend([('sub', sub_be), ('ro', ro_be), ('', root_be)])
+    router.create()
     router.open()
-    router.create()
 
     @request.addfinalizer
     def finalize():
     root_revid = router.store(dict(name=u'foo'), StringIO(''))
     sub_revid = router.store(dict(name=u'sub/bar'), StringIO(''))
 
+    router.close()
     router.destroy()
     router.create()
+    router.open()
 
     assert set(router) == existing
 

File MoinMoin/storage/middleware/_tests/test_serialization.py

View file
 from ..indexing import IndexingMiddleware
 from ..serialization import serialize, deserialize
 
-from storage.backends.stores import MutableBackend
-from storage.stores.memory import BytesStore, FileStore
+from MoinMoin.storage.backends.stores import MutableBackend
+from MoinMoin.storage.stores.memory import BytesStore, FileStore
 
 
 contents = [

File MoinMoin/storage/middleware/indexing.py

View file
 import datetime
 from StringIO import StringIO
 
-from uuid import uuid4
-make_uuid = lambda: unicode(uuid4().hex)
-
 import logging
 
 from whoosh.fields import Schema, TEXT, ID, IDLIST, NUMERIC, DATETIME, KEYWORD, BOOLEAN
 from whoosh.query import Every, Term
 from whoosh.sorting import FieldFacet
 
-from config import WIKINAME, NAME, NAME_EXACT, MTIME, CONTENTTYPE, TAGS, \
-                   LANGUAGE, USERID, ADDRESS, HOSTNAME, SIZE, ACTION, COMMENT, \
-                   CONTENT, ITEMLINKS, ITEMTRANSCLUSIONS, ACL, EMAIL, OPENID, \
-                   ITEMID, REVID
+from MoinMoin.config import WIKINAME, NAME, NAME_EXACT, MTIME, CONTENTTYPE, TAGS, \
+                            LANGUAGE, USERID, ADDRESS, HOSTNAME, SIZE, ACTION, COMMENT, \
+                            CONTENT, ITEMLINKS, ITEMTRANSCLUSIONS, ACL, EMAIL, OPENID, \
+                            ITEMID, REVID, CURRENT
+
+from MoinMoin.util.crypto import make_uuid
 
 LATEST_REVS = 'latest_revs'
 ALL_REVS = 'all_revs'
 
 
 class IndexingMiddleware(object):
-    def __init__(self, index_dir, backend, user_name=None, acl_support=False, **kw):
+    def __init__(self, index_dir, backend, wiki_name=None, **kw):
         """
         Store params, create schemas.
         """
         self.index_dir = index_dir
         self.index_dir_tmp = index_dir + '.temp'
         self.backend = backend
-        self.user_name = user_name # TODO use currently logged-in username
-        self.acl_support = acl_support
-        self.wikiname = u'' # TODO take from app.cfg.interwikiname
+        self.wikiname = wiki_name
         self.ix = {}  # open indexes
         self.schemas = {}  # existing schemas
 
             for hit in searcher.search(q, **kw):
                 doc = hit.fields()
                 latest_doc = not all_revs and doc or None
-                item = Item(self, user_name=self.user_name, latest_doc=latest_doc, itemid=doc[ITEMID])
+                item = Item(self, latest_doc=latest_doc, itemid=doc[ITEMID])
                 yield item[doc[REVID]]
 
     def search_page(self, q, all_revs=False, pagenum=1, pagelen=10, **kw):
             for hit in searcher.search_page(q, pagenum, pagelen=pagelen, **kw):
                 doc = hit.fields()
                 latest_doc = not all_revs and doc or None
-                item = Item(self, user_name=self.user_name, latest_doc=latest_doc, itemid=doc[ITEMID])
+                item = Item(self, latest_doc=latest_doc, itemid=doc[ITEMID])
                 yield item[doc[REVID]]
 
     def documents(self, all_revs=False, **kw):
         """
         for doc in self._documents(all_revs, **kw):
             latest_doc = not all_revs and doc or None
-            item = Item(self, user_name=self.user_name, latest_doc=latest_doc, itemid=doc[ITEMID])
+            item = Item(self, latest_doc=latest_doc, itemid=doc[ITEMID])
             yield item[doc[REVID]]
 
     def _documents(self, all_revs=False, **kw):
         doc = self._document(all_revs, **kw)
         if doc:
             latest_doc = not all_revs and doc or None
-            item = Item(self, user_name=self.user_name, latest_doc=latest_doc, itemid=doc[ITEMID])
+            item = Item(self, latest_doc=latest_doc, itemid=doc[ITEMID])
             return item[doc[REVID]]
 
     def _document(self, all_revs=False, **kw):
         with self.get_index(all_revs).searcher() as searcher:
             return searcher.document(**kw)
 
+    def has_item(self, name):
+        item = self[name]
+        return bool(item)
+
     def __getitem__(self, name):
         """
         Return item with <name> (may be a new or existing item).
         """
-        return Item(self, user_name=self.user_name, name=name)
+        return Item(self, name=name)
 
     def get_item(self, **query):
         """
         :kwargs **query: e.g. name=u"Foo" or itemid="..." or ...
                          (must be a unique fieldname=value for the latest-revs index)
         """
-        return Item(self, user_name=self.user_name, **query)
+        return Item(self, **query)
 
     def create_item(self, **query):
         """
         :kwargs **query: e.g. name=u"Foo" or itemid="..." or ...
                          (must be a unique fieldname=value for the latest-revs index)
         """
-        return Item.create(self, user_name=self.user_name, **query)
+        return Item.create(self, **query)
 
     def existing_item(self, **query):
         """
         :kwargs **query: e.g. name=u"Foo" or itemid="..." or ...
                          (must be a unique fieldname=value for the latest-revs index)
         """
-        return Item.existing(self, user_name=self.user_name, **query)
+        return Item.existing(self, **query)
 
 
 class Item(object):
-    def __init__(self, indexer, user_name=None, latest_doc=None, **query):
+    def __init__(self, indexer, latest_doc=None, **query):
         """
         :param indexer: indexer middleware instance
-        :param user_name: user name (for acl checking)
         :param latest_doc: if caller already has a latest-revs index whoosh document
                            it can be given there, to avoid us fetching same doc again
                            from the index
                          doc from the index (if not given via latest_doc).
         """
         self.indexer = indexer
-        self.user_name = user_name
         self.backend = self.indexer.backend
         if latest_doc is None:
             # we need to call the method without acl check to avoid endless recursion:
     def acl(self):
         return self._current.get(ACL)
 
+    @property
+    def name(self):
+        return self._current.get(NAME, 'DoesNotExist')
+
     @classmethod
-    def create(cls, indexer, user_name=None, **query):
+    def create(cls, indexer, **query):
         """
         Create a new item and return it, raise exception if it already exists.
         """
-        item = cls(indexer, user_name=user_name, **query)
+        item = cls(indexer, **query)
         if not item:
             return item
         raise ItemAlreadyExists(repr(query))
 
     @classmethod
-    def existing(cls, indexer, user_name=None, **query):
+    def existing(cls, indexer, **query):
         """
         Get an existing item and return it, raise exception if it does not exist.
         """
-        item = cls(indexer, user_name=user_name, **query)
+        item = cls(indexer, **query)
         if item:
             return item
         raise ItemDoesNotExist(repr(query))
         """
         Get Revision with revision id <revid>.
         """
+        if revid == CURRENT:
+            revid = self._current.get(REVID)
+            if revid is None:
+                raise KeyError
         rev = Revision(self, revid)
         rev.data # XXX trigger KeyError if rev does not exist
         return rev
         # If you access data or meta, it will, though.
 
     @property
+    def name(self):
+        return self.meta.get(NAME, 'DoesNotExist')
+
+    @property
     def data(self):
         if self._data is None:
             meta, data = self.backend.retrieve(self.revid) # raises KeyError if rev does not exist
         return cmp(self.meta, other.meta)
 
 
-class Meta(object):
+from collections import Mapping
+
+class Meta(Mapping):
     def __init__(self, revision, doc, meta=None):
         self.revision = revision
         self._doc = doc or {}
         else:
             return True
 
+    def __iter__(self):
+        self._meta, self.revision._data = self.revision.backend.retrieve(self.revision.revid) # raises KeyError if rev does not exist
+        return iter(self._meta)
+
     def __getitem__(self, key):
         try:
             return self._meta[key]
             return 0
         return cmp(self[MTIME], other[MTIME])
 
+    def __len__(self):
+        return 0 # XXX
+
+    def __repr__(self):
+        return "Meta _doc: %r _meta: %r" % (self._doc, self._meta)
+

File MoinMoin/storage/middleware/protecting.py

View file
 
 import logging
 
-from config import ACL, CREATE, READ, WRITE, OVERWRITE, DESTROY, ADMIN
+from MoinMoin.config import ACL, CREATE, READ, WRITE, OVERWRITE, DESTROY, ADMIN
+from MoinMoin.security import AccessControlList
 
 
 class AccessDenied(Exception):
 
 
 class ProtectingMiddleware(object):
-    def __init__(self, indexer, user_name):
+    def __init__(self, indexer, user, acl_mapping):
         """
         :param indexer: indexing middleware instance
         :param user_name: the user's name (used for checking permissions)
+        :param acl_mapping: list of (name_prefix, acls) tuples, longest prefix first, '' last
+                            acls = dict with before, default, after, hierarchic entries
         """
         self.indexer = indexer
-        self.user_name = user_name
+        self.user = user
+        self.acl_mapping = acl_mapping
+        self.valid_rights = ['read', 'write', 'create', 'admin', 'overwrite', 'destroy', ]
+
+    def get_acls(self, itemname):
+        for prefix, acls in self.acl_mapping:
+            if itemname.startswith(prefix):
+                return acls
+        else:
+            raise ValueError('No acl_mapping entry found for item %r' % itemname)
 
     def search(self, q, all_revs=False, **kw):
         for rev in self.indexer.search(q, all_revs, **kw):
             if rev.allows(READ):
                 return rev
 
+    def has_item(self, name):
+        return self.indexer.has_item(name)
+
     def __getitem__(self, name):
         item = self.indexer[name]
         return ProtectedItem(self, item)
         item = self.indexer.existing_item(**query)
         return ProtectedItem(self, item)
 
+    def may(self, itemname, capability, username=None):
+        item = self[itemname]
+        allowed = item.allows(capability, user_name=username)
+        return allowed
+
 
 class ProtectedItem(object):
     def __init__(self, protector, item):
     def itemid(self):
         return self.item.itemid
 
+    @property
+    def name(self):
+        return self.item.name
+
     def __nonzero__(self):
         return bool(self.item)
 
-    def allows(self, capability):
+    def _allows(self, right, user_name):
         """
-        check latest ACL whether capability is allowed
+        check permissions in this item without considering before/after acls
         """
-        # TODO: this is just a temporary hack to be able to test this without real ACL code,
-        # replace it by a sane one later.
-        # e.g. acl = "joe:read"  --> user joe may read
+        acls = self.protector.get_acls(self.item.name)
         acl = self.item.acl
-        user_name = self.protector.user_name
-        if acl is None or user_name is None:
-            allow = True
+        if acl is not None:
+            # 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.
+            acl = AccessControlList([acl, ], acls['default'], valid=self.protector.valid_rights)
+            allowed = acl.may(user_name, right)
+            if allowed is not None:
+                return allowed
         else:
-            allow = "%s:%s" % (user_name, capability) in acl
-        #print "item allows user '%s' to '%s' (acl: %s): %s" % (user_name, capability, acl, ["no", "yes"][allow])
-        return allow
+            if acls['hierarchic']:
+                # check parent(s), recursively
+                parent_tail = self.item.name.rsplit('/', 1)
+                if len(parent_tail) == 2:
+                    parent, _ = parent_tail
+                    parent_item = self.protector[parent]
+                    allowed = parent_item._allows(right, user_name)
+                    if allowed is not None:
+                        return allowed
+
+            acl = AccessControlList([acls['default'], ], valid=self.protector.valid_rights)
+            allowed = acl.may(user_name, right)
+            if allowed is not None:
+                return allowed
+
+    def allows(self, right, user_name=None):
+        """ Check if 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
+        :param username: username to use for permissions check (default is to
+                         use the username doing the current request)
+        :rtype: bool
+        :returns: True if you have permission or False
+        """
+        if user_name is None:
+            user_name = self.protector.user.name
+
+        acls = self.protector.get_acls(self.item.name)
+
+        before = AccessControlList([acls['before'], ], valid=self.protector.valid_rights)
+        allowed = before.may(user_name, right)
+        if allowed is not None:
+            return allowed
+
+        allowed = self._allows(right, user_name)
+        if allowed is not None:
+            return allowed
+
+        after = AccessControlList([acls['after'], ], valid=self.protector.valid_rights)
+        allowed = after.may(user_name, right)
+        if allowed is not None:
+            return allowed
+
+        return False
 
     def require(self, capability):
         if not self.allows(capability):
-            raise AccessDenied("item does not allow user '%r' to '%r'" % (self.protector.user_name, capability))
+            raise AccessDenied("item does not allow user '%r' to '%r'" % (self.protector.user.name, capability))
 
     def iter_revs(self):
         self.require(READ)

File MoinMoin/storage/middleware/routing.py

View file
 
 from config import NAME
 
-from storage.backends import BackendBase, MutableBackendBase
+from MoinMoin.storage.backends import BackendBase, MutableBackendBase
 
 
 class Backend(MutableBackendBase):
                 yield u'%s/%s' % (mountpoint, revid)
 
     def retrieve(self, revid):
+        print revid
         mountpoint, revid = revid.rsplit(u'/', 1)
         backend = self._get_backend(mountpoint)[0]
         meta, data = backend.retrieve(revid)

File MoinMoin/storage/stores/_tests/conftest.py

View file
 import pytest
 from ..wrappers import ByteToStreamWrappingStore
 
-STORES_PACKAGE = 'storage.stores'
+STORES_PACKAGE = 'MoinMoin.storage.stores'
 
 STORES = 'fs kc kt memory sqlite sqlite:compressed sqla'.split()
 
 
 def pytest_generate_tests(metafunc):
     argnames = metafunc.funcargnames
-    
+
     if 'store' in argnames:
         klasses = 'BytesStore', 'FileStore'
     elif 'bst' in argnames:

File MoinMoin/storage/stores/_tests/test_memory.py

View file
     assert store._st is None
 
     store.create()
+    store.open()
     assert store._st == {}
 
     return store
 @pytest.mark.multi(Store=[BytesStore, FileStore])
 def test_destroy(Store):
     store = test_create(Store)
+    store.close()
     store.destroy()
     assert store._st is None
 

File MoinMoin/storage/stores/kc.py

View file
 
 Note: only ONE process can open a kyoto cabinet in OWRITER (writable) mode.
       Multithreading is allowed, but not multi-process.
-      
+
       For multi-process, you either need to use some different store (not
       kyoto cabinet) or use a store for kyoto tycoon (which is a network
       server that uses kyoto cabinet).

File MoinMoin/storage/stores/memory.py

View file
 
     def __init__(self):
         self._st = None
+        self.__st = None
 
     def create(self):
-        self._st = {}
+        self.__st = {}
 
     def destroy(self):
+        self.__st = None
+
+    def open(self):
+        self._st = self.__st
+
+    def close(self):
         self._st = None
 
     def __iter__(self):

File wikiconfig.py

View file
 import sys, os
 
 from MoinMoin.config.default import DefaultConfig
-from MoinMoin.storage.backends import create_simple_mapping
+from MoinMoin.storage import create_simple_mapping
 from MoinMoin.util.interwiki import InterWikiMap
 
 
     instance_dir = os.path.join(wikiconfig_dir, 'wiki')
     data_dir = os.path.join(instance_dir, 'data') # Note: this used to have a trailing / in the past
     index_dir = os.path.join(instance_dir, "index")
-    index_dir_tmp = os.path.join(instance_dir, "index_tmp")
 
     # This provides a simple default setup for your backend configuration.
     # 'fs:' indicates that you want to use the filesystem backend. You can also use
     # 'hg:' instead to indicate that you want to use the mercurial backend.
     # Alternatively you can set up the mapping yourself (see HelpOnStorageConfiguration).
-    namespace_mapping = create_simple_mapping(
-                            backend_uri='fs2:%s/%%(nsname)s' % data_dir,
+    namespace_mapping, acl_mapping = create_simple_mapping(
+                            uri='stores:fs:%s/%%(nsname)s/%%%%(kind)s' % data_dir,
                             # XXX we use rather relaxed ACLs for the development wiki:
                             content_acl=dict(before=u'',
                                              default=u'All:read,write,create,destroy,admin',