[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