[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/locking/ - Hook security up

Gary Poster gary at zope.com
Wed Nov 9 13:00:29 EST 2005


Log message for revision 40001:
  - Hook security up
  - Correct some interfaces, and verify them in the tests.
  - Add Persistent to LockInfo
  - Make some of the package layout a bit more in line with the current best practices
  
  

Changed:
  U   Zope3/trunk/src/zope/app/locking/README.txt
  U   Zope3/trunk/src/zope/app/locking/adapter.py
  U   Zope3/trunk/src/zope/app/locking/configure.zcml
  U   Zope3/trunk/src/zope/app/locking/interfaces.py
  U   Zope3/trunk/src/zope/app/locking/lockinfo.py
  U   Zope3/trunk/src/zope/app/locking/storage.py
  U   Zope3/trunk/src/zope/app/locking/tests.py

-=-
Modified: Zope3/trunk/src/zope/app/locking/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/locking/README.txt	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/README.txt	2005-11-09 18:00:29 UTC (rev 40001)
@@ -20,7 +20,7 @@
     provide fields that higher-level application components can use
     to implement and enforce such semantics
 
-  - can potentially be be used to build more ambitious locking
+  - can potentially be used to build more ambitious locking
     mechanisms (such as WebDAV locking equivalent to Zope 2)
 
   - supports common use cases that have been uncovered in several years
@@ -140,8 +140,10 @@
 have to adapt an object to `ILockable`:
 
   >>> obj = ILockable(item1)
+  >>> from zope.interface.verify import verifyObject
+  >>> verifyObject(ILockable, obj)
+  True
 
-
 We can ask if the object is locked:
 
   >>> obj.locked()
@@ -158,7 +160,7 @@
 of an object that implements `ILockInfo` on success:
 
   >>> info = obj.lock()
-  >>> ILockInfo.providedBy(info)
+  >>> verifyObject(ILockInfo, info)
   True
 
   >>> obj.locked()
@@ -240,10 +242,8 @@
   >>> obj.locked()
   False
 
-  >>> # undo our time hack
-  >>> zope.app.locking.storage.timefunc = time.time
+(Note that we undo our time hack in the tearDown of this module.)
 
-
 Finally, it is possible to explicitly get an `ILockInfo` object that
 contains the lock information for the object. Note that locks that do
 not have a timeout set have a timeout value of `None`.
@@ -300,6 +300,8 @@
   >>> from zope.app.locking.interfaces import ILockTracker
   >>> from zope.app.zapi import getUtility
   >>> util = getUtility(ILockTracker)
+  >>> verifyObject(ILockTracker, util)
+  True
 
   >>> items = util.getLocksForPrincipal('britney')
   >>> len(items) == 1
@@ -322,7 +324,17 @@
   >>> len(items)
   0
 
+The lock storage utility provides further capabilities, and is a part of the
+standard lock adapter implementation, but the ILockable interface does not
+depend on ILockStorage.  Other implementations of ILockable may not use
+ILockStorage.  However, if used by the adapter, it provides useful
+capabilties.
 
+  >>> from zope.app.locking.interfaces import ILockStorage
+  >>> util = getUtility(ILockStorage)
+  >>> verifyObject(ILockStorage, util)
+  True
+
 Locking events
 --------------
 

Modified: Zope3/trunk/src/zope/app/locking/adapter.py
===================================================================
--- Zope3/trunk/src/zope/app/locking/adapter.py	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/adapter.py	2005-11-09 18:00:29 UTC (rev 40001)
@@ -17,22 +17,16 @@
 $Id: $
 """
 
-from zope.app.locking.interfaces import ILockable, ILockedEvent
-from zope.app.locking.interfaces import IUnlockedEvent, IBreakLockEvent
+from zope import interface, component, event
+import zope.security.management
 from zope.app.keyreference.interfaces import IKeyReference
-from zope.component.exceptions import ComponentLookupError
-from zope.app.event.objectevent import ObjectEvent
-from zope.app.locking.interfaces import LockingError
-from zope.app.locking.storage import ILockStorage
+from zope.app.i18n import ZopeMessageFactory as _
+
 from zope.app.locking.lockinfo import LockInfo
-from zope.app.locking.interfaces import _
-from zope.component import getUtility
-import zope.security.management
-from zope.event import notify
-import zope.interface
+from zope.app.locking import interfaces
 
-
-
+ at component.adapter(interface.Interface)
+ at interface.implementer(interfaces.ILockable)
 def LockingAdapterFactory(target):
     """
     Return target adapted to ILockable, or None. This should be registered
@@ -41,21 +35,20 @@
     if IKeyReference(target, None) is None:
         return None
     return LockingAdapter(target)
-    
 
 class LockingAdapter(object):
     """
     Default ILockable adapter implementation.
     """
 
-    zope.interface.implements(ILockable)
+    # this MUST be a trusted adapter!!
+
+    interface.implements(interfaces.ILockable)
     
     def __init__(self, context):
-        try:
-            self.storage = getUtility(ILockStorage, context=context)
-        except ComponentLookupError:
-            self.storage = getUtility(ILockStorage)
+        self.storage = component.getUtility(interfaces.ILockStorage)
         self.context = context
+        self.__parent__ = context
 
     def _findPrincipal(self):
         # Find the current principal. Note that it is possible for there
@@ -66,39 +59,39 @@
             if principal is None:
                 principal = p.principal
             else:
-                raise LockingError(_("Multiple principals found"))
+                raise interfaces.LockingError(_("Multiple principals found"))
         if principal is None:
-            raise LockingError(_("No principal found"))
+            raise interfaces.LockingError(_("No principal found"))
         return principal
 
-    def lock(self, principal=None, timeout=None):
+    def lock(self, timeout=None, principal=None):
         if principal is None:
             principal = self._findPrincipal()
         principal_id = principal.id
         lock = self.storage.getLock(self.context)
         if lock is not None:
-            raise LockingError(_("Object is already locked"))
+            raise interfaces.LockingError(_("Object is already locked"))
         lock = LockInfo(self.context, principal_id, timeout)
         self.storage.setLock(self.context, lock)
-        notify(LockedEvent(self.context, lock))
+        event.notify(interfaces.LockedEvent(self.context, lock))
         return lock
 
     def unlock(self):
         lock = self.storage.getLock(self.context)
         if lock is None:
-            raise LockingError(_("Object is not locked"))
+            raise interfaces.LockingError(_("Object is not locked"))
         principal = self._findPrincipal()
         if lock.principal_id != principal.id:
-            raise LockingError(_("Principal is not lock owner"))
+            raise interfaces.LockingError(_("Principal is not lock owner"))
         self.storage.delLock(self.context)
-        notify(UnlockedEvent(self.context))
+        event.notify(interfaces.UnlockedEvent(self.context))
 
     def breaklock(self):
         lock = self.storage.getLock(self.context)
         if lock is None:
-            raise LockingError(_("Object is not locked"))
+            raise interfaces.LockingError(_("Object is not locked"))
         self.storage.delLock(self.context)
-        notify(BreakLockEvent(self.context))
+        event.notify(interfaces.BreakLockEvent(self.context))
 
     def locked(self):
         lock = self.storage.getLock(self.context)
@@ -130,30 +123,9 @@
     def __repr__(self):
         return '<Locking adapter for %s>' % repr(self.context)
 
-
-
-class EventBase(ObjectEvent):
-    def __repr__(self):
-        return '%s for %s' % (self.__class__.__name__, `self.object`)
-
-class LockedEvent(EventBase):
-    zope.interface.implements(ILockedEvent)
-
-    def __init__(self, object, lock):
-        self.object = object
-        self.lock = lock
-
-
-class UnlockedEvent(EventBase):
-    zope.interface.implements(IUnlockedEvent)
-
-class BreakLockEvent(UnlockedEvent):
-    zope.interface.implements(IBreakLockEvent)
-
-
 class LockingPathAdapter(object):
 
-    zope.interface.implements(
+    interface.implements(
         zope.app.traversing.interfaces.IPathAdapter)
 
     def __init__(self, target):

Modified: Zope3/trunk/src/zope/app/locking/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/locking/configure.zcml	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/configure.zcml	2005-11-09 18:00:29 UTC (rev 40001)
@@ -1,8 +1,6 @@
 <configure xmlns="http://namespaces.zope.org/zope"
            i18n_domain="zope.app.locking">
 
-  <permission id="zope.app.locking.UseLocking" title="Use locking" />
-
   <!-- Registering documentation with API doc -->
   <configure
       xmlns:apidoc="http://namespaces.zope.org/apidoc"
@@ -17,6 +15,30 @@
 
   </configure>
 
+  <class class=".adapter.LockingAdapter">
+    <allow attributes="locked ownLock isLockedOut getLockInfo" />
+    <require permission="zope.View"
+      attributes="locker" />
+    <require permission="zope.ManageContent"
+      attributes="lock unlock" />
+    <require permission="zope.Security"
+      attributes="breaklock" />
+  </class>
+
+  <class class=".lockinfo.LockInfo">
+    <allow attributes="getLockInfo" />
+    <require permission="zope.View"
+      attributes="principal_id created timeout" />
+    <require permission="zope.View"
+      interface="zope.interface.common.mapping.IEnumerableMapping" 
+      />
+    <require permission="zope.View"
+      interface="zope.interface.common.mapping.IWriteMapping"
+      />
+  </class>
+
+  <adapter factory=".adapter.LockingAdapterFactory" trusted="1" />
+
   <adapter
       factory=".storage.Sized"
       trusted="yes"

Modified: Zope3/trunk/src/zope/app/locking/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/locking/interfaces.py	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/interfaces.py	2005-11-09 18:00:29 UTC (rev 40001)
@@ -16,25 +16,20 @@
 
 $Id: $
 """
+from zope import interface, schema
 
-from zope.app.annotation.interfaces import IAttributeAnnotatable
 from zope.app.event.interfaces import IObjectEvent
-from zope.interface import Interface, Attribute
-from zope.i18nmessageid import MessageFactory
 from zope.interface.common.mapping import IMapping
-import zope.interface
-import zope.schema
+from zope.app.event.objectevent import ObjectEvent
+from zope.app.i18n import ZopeMessageFactory as _
 
-_ = MessageFactory('zope.app.locking')
-
-
-class ILockable(Interface):
+class ILockable(interface.Interface):
     """
     The ILockable interface defines the locking operations that are
     supported for lockable objects.
     """
 
-    def lock(timeout=None):
+    def lock(principal=None, timeout=None):
         """
         Lock the object in the name of the current principal. This method
         raises a LockingError if the object cannot be locked by the current
@@ -49,7 +44,9 @@
 
     def breaklock():
         """
-        Break all existing locks on an object for all principals.
+        Break the lock on the object, regardless of whether the current
+        principal created the lock.  Raises a LockingError if there is not a
+        lock on the object
         """
 
     def locked():
@@ -63,10 +60,9 @@
         the object, or None if the object is not locked.
         """
 
-    def getLockInfo(obj):
+    def getLockInfo():
         """
-        Return a (possibly empty) sequence of ILockInfo objects describing
-        the current locks on the object.
+        Return an ILockInfo describing the current lock or None.
         """
 
     def ownLock():
@@ -81,7 +77,7 @@
         """
 
 
-class ILockTracker(Interface):
+class ILockTracker(interface.Interface):
     """
     An ILockTracker implementation is responsible for tracking what
     objects are locked within its scope.
@@ -103,29 +99,53 @@
     An ILockInfo implementation is responsible for 
     """
 
-    def getObject():
-        """Return the actual locked object."""
+    target = interface.Attribute("""the actual locked object.""")
 
-    creator = zope.schema.TextLine(
+    principal_id = schema.TextLine(
         description=_("id of the principal owning the lock")
         )
 
-    created = zope.schema.Float(
+    created = schema.Float(
         description=_("time value indicating the creation time"),
         required=False
         )
 
-    timeout = zope.schema.Float(
+    timeout = schema.Float(
         description=_("time value indicating the lock timeout from creation"),
         required=False
         )
 
+class ILockStorage(interface.Interface):
+    """
+    A lock storage lets you store information about locks in a central place
+    """
+                    
+    def getLock(object):
+        """
+        Get the current lock for an object.
+        """
 
+    def setLock(object, lock):
+        """
+        Set the current lock for an object.
+        """
 
+    def delLock(object):
+        """
+        Delete the current lock for an object.
+        """
+
+    def cleanup():
+        """We occasionally want to clean up expired locks to keep them
+        from accumulating over time and slowing things down.
+        """
+
+# event interfaces
+
 class ILockedEvent(IObjectEvent):
     """An object has been locked"""
 
-    lock = Attribute("The lock set on the object")
+    lock = interface.Attribute("The lock set on the object")
     
 class IUnlockedEvent(IObjectEvent):
     """An object has been unlocked"""
@@ -133,8 +153,28 @@
 class IBreakLockEvent(IUnlockedEvent):
     """Lock has been broken on an object"""
 
+# events
 
+class EventBase(ObjectEvent):
+    def __repr__(self):
+        return '%s for %s' % (self.__class__.__name__, `self.object`)
 
+class LockedEvent(EventBase):
+    interface.implements(ILockedEvent)
+
+    def __init__(self, object, lock):
+        self.object = object
+        self.lock = lock
+
+
+class UnlockedEvent(EventBase):
+    interface.implements(IUnlockedEvent)
+
+class BreakLockEvent(UnlockedEvent):
+    interface.implements(IBreakLockEvent)
+
+# exceptions
+
 class LockingError(Exception):
     """
     The exception raised for locking errors.

Modified: Zope3/trunk/src/zope/app/locking/lockinfo.py
===================================================================
--- Zope3/trunk/src/zope/app/locking/lockinfo.py	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/lockinfo.py	2005-11-09 18:00:29 UTC (rev 40001)
@@ -16,49 +16,25 @@
 
 $Id: $
 """
+import time
+import persistent.mapping
+import zope.interface
+from zope.app.locking.interfaces import ILockInfo
 
-from zope.app.locking.interfaces import ILockInfo, LockingError
-import zope.interface, time
+class LockInfo(persistent.mapping.PersistentMapping):
 
-
-class LockInfo(object):
-    
     zope.interface.implements(ILockInfo)
 
     def __init__(self, target, principal_id, timeout=None):
-        self.target = target
+        # must not store target with security proxy.
+        super(LockInfo, self).__init__()
+        self.__parent__ = self.target = target
         self.principal_id = principal_id
         self.created = time.time()
         self.timeout = timeout
-        self.data = {}
 
-    def get(self, key, default=None):
-        return self.data.get(key, default)
-
-    def keys(self):
-        return self.data.keys()
-
-    def values(self):
-        return self.data.values()
-
-    def items(self):
-        return self.data.items()
-
-    def __getitem__(self, key):
-        return self.data[key]
-
-    def __setitem__(self, key, value):
-        self.data[key] = value
-
-    def __delitem__(self, key):
-        del self.data[key]
-
-    def __contains__(self, key):
-        return key in self.data
-
-    def __iter__(self):
-        return iter(self.data)
-
-    def __len__(self):
-        return len(self.data)
-
+    def __repr__(self):
+        return "<%s.%s object at 0x%x>" % (
+            self.__class__.__module__,
+            self.__class__.__name__,
+            id(self))

Modified: Zope3/trunk/src/zope/app/locking/storage.py
===================================================================
--- Zope3/trunk/src/zope/app/locking/storage.py	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/storage.py	2005-11-09 18:00:29 UTC (rev 40001)
@@ -24,12 +24,12 @@
 from BTrees.OOBTree import OOBTree
 from BTrees.IOBTree import IOBTree
 
-import zope.component
-import zope.interface
+from zope import component, interface
 
 from zope.app.keyreference.interfaces import IKeyReference
-from zope.app.locking.interfaces import ILockTracker
-from zope.app.locking.interfaces import LockingError
+from zope.app.locking import interfaces
+# for backwards compatibility:
+from zope.app.locking.interfaces import ILockStorage
 from zope.app.size.interfaces import ISized
 
 from zope.app.i18n import ZopeMessageFactory as _
@@ -38,14 +38,7 @@
 timefunc = time.time
 
 
-class ILockStorage(zope.interface.Interface):
-    """
-    This interface is internal to the default locking implementation. It
-    lets us store lock information in a central place rather than store
-    it on individual objects.
-    """
 
-
 class LockStorage(object):
     # WARNING: This is not persistent.  Use PersistentLockStorage instead.
     # This class must remain so that existing instances can be unpickled
@@ -67,7 +60,7 @@
 
     """
 
-    zope.interface.implements(ILockStorage, ILockTracker)
+    interface.implements(interfaces.ILockStorage, interfaces.ILockTracker)
     
     def __init__(self):
         self.timeouts = IOBTree()
@@ -81,15 +74,15 @@
     # ILockTracker implementation
     
     def getLocksForPrincipal(self, principal_id):
-        return self.currentLocks(principal_id)
+        return self._currentLocks(principal_id)
 
     def getAllLocks(self):
-        return self.currentLocks()
+        return self._currentLocks()
 
-    # ILockStorage implementation
-
-    def currentLocks(self, principal_id=None):
+    def _currentLocks(self, principal_id=None):
         """
+        Helper method for getAllLocks and getLocksForPrincipal.
+        
         Return the currently active locks, possibly filtered by principal.
         """
         result = []
@@ -100,6 +93,8 @@
                     ):
                     result.append(lock)
         return result
+
+    # ILockStorage implementation
                     
     def getLock(self, object):
         """
@@ -145,8 +140,8 @@
 
 class Sized(object):
 
-    zope.interface.implements(ISized)
-    zope.component.adapts(ILockStorage)
+    interface.implements(ISized)
+    component.adapts(interfaces.ILockStorage)
 
     def __init__(self, context):
         self.context = context

Modified: Zope3/trunk/src/zope/app/locking/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/locking/tests.py	2005-11-09 13:26:18 UTC (rev 40000)
+++ Zope3/trunk/src/zope/app/locking/tests.py	2005-11-09 18:00:29 UTC (rev 40001)
@@ -82,6 +82,8 @@
 
 
 def tearDown(test):
+    import zope.app.locking.storage
+    import time
     del sys.modules[name]
     abort()
     db = test.globs.get('db')
@@ -90,6 +92,7 @@
     ps.tearDown()
     del test._storage
     zope.event.subscribers.pop()
+    zope.app.locking.storage.timefunc = time.time
 
 def test_suite():
     return doctest.DocFileSuite('README.txt', setUp=setUp, tearDown=tearDown,



More information about the Zope3-Checkins mailing list