跳转到主要内容

建议性独占锁、共享锁和冻结(锁定为无人)。

项目描述

建议性独占锁、共享锁和冻结(锁定为无人)。

zope.locking 包提供了三个主要功能:

  • 对单个对象的咨询排他锁;

  • 对单个对象的咨询共享锁;以及

  • 冻结对象(锁定给任何人)。

锁和冻结本身是咨询令牌,本身没有意义。它们必须由其他软件赋予意义,例如安全策略。

本包主要从系统API的角度来处理这些功能,大部分不受策略约束;然后提供一组适配器,以更常见的交互方式与用户进行交互,并有一些访问策略。我们首先将查看系统API,然后解释提供的适配器的策略和建议的使用。

系统API

该包的核心方法是将锁和冻结令牌创建并由令牌实用工具注册。令牌只有在注册后才能工作。这可以确切地知道,从而操纵系统中的所有活动令牌。

然后,我们将介绍第一个对象,即TokenUtility:负责注册和检索令牌的实用工具。

>>> from zope import component, interface
>>> from zope.locking import interfaces, utility, tokens
>>> util = utility.TokenUtility()
>>> from zope.interface.verify import verifyObject
>>> verifyObject(interfaces.ITokenUtility, util)
True

该实用工具只有几个方法–getiterForPrincipalId__iter__register,我们将在下面查看。它应该持久化,并且包含的实现实际上是持久的。持久化并期望作为本地实用工具安装。实用工具在注册持久令牌之前需要与数据库建立连接。

>>> from zope.locking.testing import Demo
>>> lock = tokens.ExclusiveLock(Demo(), 'Fantomas')
>>> util.register(lock)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'add'
>>> conn = get_connection()
>>> conn.add(util)

如果令牌提供了IPersistent,实用工具将把它添加到它的连接中。

>>> lock._p_jar is None
True
>>> lock = util.register(lock)
>>> lock._p_jar is util._p_jar
True
>>> lock.end()
>>> lock = util.register(lock)

标准令牌实用工具可以接受任何可适配到IKeyReference的对象的令牌。

>>> import datetime
>>> import pytz
>>> before_creation = datetime.datetime.now(pytz.utc)
>>> demo = Demo()

现在,使用demo类的实例,可以使用令牌实用工具为demo实例注册锁和冻结令牌。

如上所述,创建锁或冻结令牌的一般模式是先创建它–此时大多数方法和属性都不可用–然后将其注册到令牌实用工具。注册后,锁就有效并就位。

TokenUtility实际上可以与实现zope.locking.interfaces.IAbstractToken的任何东西一起使用,但我们将查看zope.locking包附带的前四个令牌:排他锁、共享锁、永久冻结和可结束冻结。

排他锁

排他锁是只由单个主体拥有的令牌。不能添加或删除任何主体:锁令牌必须结束并启动另一个,以便另一个主体能够获得锁的好处(无论它们被配置为什么)。

以下是一个创建和注册排他锁的示例:id为‘john’的主体锁定demo对象。

>>> lock = tokens.ExclusiveLock(demo, 'john')
>>> res = util.register(lock)
>>> res is lock
True

锁令牌现在生效。注册令牌(锁)触发了一个ITokenStartedEvent,我们现在将探讨它。

(注意,此示例使用事件列表来查看已触发的事件。这是一个简单的列表,其append方法已添加为zope.event.subscribers列表的订阅者。当此文件作为测试运行时,它包含为全局。)

>>> from zope.component.eventtesting import events
>>> ev = events[-1]
>>> verifyObject(interfaces.ITokenStartedEvent, ev)
True
>>> ev.object is lock
True

现在锁令牌已创建并注册,令牌实用工具知道它。实用工具的get方法只是简单地返回对象的活动令牌或None——它永远不会返回已结束的令牌,实际上,所有实用工具方法都不这样做。

>>> util.get(demo) is lock
True
>>> util.get(Demo()) is None
True

注意,get接受交替的默认值,类似于dictionary.get

>>> util.get(Demo(), util) is util
True

iterForPrincipalId方法返回给定主体ID的活动锁的迭代器。

>>> list(util.iterForPrincipalId('john')) == [lock]
True
>>> list(util.iterForPrincipalId('mary')) == []
True

实用工具的__iter__方法只是遍历所有活动(非结束)令牌。

>>> list(util) == [lock]
True

令牌实用工具不允许为同一对象注册多个活动令牌。

>>> util.register(tokens.ExclusiveLock(demo, 'mary'))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...
>>> util.register(tokens.SharedLock(demo, ('mary', 'jane')))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...
>>> util.register(tokens.Freeze(demo))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...

还值得看一下锁令牌本身。已注册的锁令牌实现了IExclusiveLock。

>>> verifyObject(interfaces.IExclusiveLock, lock)
True

它提供了一系列功能。或许最重要的属性是令牌是否生效:ended。此令牌是活动的,因此它尚未结束

>>> lock.ended is None
True

当它结束时,ended属性是UTC时间戳,表示令牌何时结束。我们将在下面演示。

稍后,creationexpirationdurationremaining_duration将很重要;现在我们只注意它们的存在。

>>> before_creation <= lock.started <= datetime.datetime.now(pytz.utc)
True
>>> lock.expiration is None # == forever
True
>>> lock.duration is None # == forever
True
>>> lock.remaining_duration is None # == forever
True

end方法和相关的结束和过期属性都是IEndable接口的一部分——不是所有令牌都必须实现的接口,我们稍后也将讨论。

>>> interfaces.IEndable.providedBy(lock)
True

context__parent__属性指向被锁定的对象——在我们的示例中是demo。《context》是获取对象的预期标准API,但《__parent__》对于Zope 3安全设置很重要,如本文档末尾所述。

>>> lock.context is demo
True
>>> lock.__parent__ is demo # important for security
True

将锁注册到令牌实用工具时设置了实用工具属性,并将开始属性初始化为锁开始的日期时间。除了令牌实用工具之外,任何代码都不应设置实用工具属性。

>>> lock.utility is util
True

令牌始终提供一个principal_ids属性,该属性提供了一个令牌中包含的主体的可迭代对象。在我们的例子中,这是对‘john’的排他锁,因此值很简单。

>>> sorted(lock.principal_ids)
['john']

基本令牌(如排他锁)上的唯一方法是end。不带参数调用它将永久且明确地结束令牌的生命。

>>> lock.end()

像注册令牌一样,结束令牌会触发一个事件。

>>> ev = events[-1]
>>> verifyObject(interfaces.ITokenEndedEvent, ev)
True
>>> ev.object is lock
True

它影响令牌上的属性。同样,其中最重要的是ended,现在是结束的日期时间。

>>> lock.ended >= lock.started
True
>>> lock.remaining_duration == datetime.timedelta()
True

它还会影响对令牌实用工具的查询。

>>> util.get(demo) is None
True
>>> list(util.iterForPrincipalId('john')) == []
True
>>> list(util) == []
True

不要尝试结束已结束的令牌。

>>> lock.end()
Traceback (most recent call last):
...
zope.locking.interfaces.EndedError

结束令牌的另一种方式是使用过期日期时间。我们将看到,处理超时时最重要的一点是,由于超时而过期的令牌不会触发任何过期事件。它只是开始为ended属性提供expiration值。

>>> one = datetime.timedelta(hours=1)
>>> two = datetime.timedelta(hours=2)
>>> three = datetime.timedelta(hours=3)
>>> four = datetime.timedelta(hours=4)
>>> lock = util.register(tokens.ExclusiveLock(demo, 'john', three))
>>> lock.duration
datetime.timedelta(seconds=10800)
>>> three >= lock.remaining_duration >= two
True
>>> lock.ended is None
True
>>> util.get(demo) is lock
True
>>> list(util.iterForPrincipalId('john')) == [lock]
True
>>> list(util) == [lock]
True

可结束令牌的过期时间始终是创建日期加上超时时间。

>>> lock.expiration == lock.started + lock.duration
True
>>> ((before_creation + three) <=
...  (lock.expiration) <= # this value is the expiration date
...  (before_creation + four))
True

可以在锁仍然活动时更改过期时间,使用任何expirationremaining_durationduration属性。所有更改都会触发事件。首先,我们将更改过期属性。

>>> lock.expiration = lock.started + one
>>> lock.expiration == lock.started + one
True
>>> lock.duration == one
True
>>> ev = events[-1]
>>> verifyObject(interfaces.IExpirationChangedEvent, ev)
True
>>> ev.object is lock
True
>>> ev.old == lock.started + three
True

然后,我们将更改持续时间属性。

>>> lock.duration = four
>>> lock.duration
datetime.timedelta(seconds=14400)
>>> four >= lock.remaining_duration >= three
True
>>> ev = events[-1]
>>> verifyObject(interfaces.IExpirationChangedEvent, ev)
True
>>> ev.object is lock
True
>>> ev.old == lock.started + one
True

现在,我们将修改我们的代码,使其认为它是两小时后,然后检查并修改剩余持续时间属性。

>>> def hackNow():
...     return (datetime.datetime.now(pytz.utc) +
...             datetime.timedelta(hours=2))
...
>>> import zope.locking.utils
>>> oldNow = zope.locking.utils.now
>>> zope.locking.utils.now = hackNow # make code think it's 2 hours later
>>> lock.duration
datetime.timedelta(seconds=14400)
>>> two >= lock.remaining_duration >= one
True
>>> lock.remaining_duration -= one
>>> one >= lock.remaining_duration >= datetime.timedelta()
True
>>> three + datetime.timedelta(minutes=1) >= lock.duration >= three
True
>>> ev = events[-1]
>>> verifyObject(interfaces.IExpirationChangedEvent, ev)
True
>>> ev.object is lock
True
>>> ev.old == lock.started + four
True

现在,我们将修改我们的代码,使其认为它是一天后。非常重要的一点是记住,带有超时的锁无声结束——也就是说,不会触发事件。

>>> def hackNow():
...     return (
...         datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1))
...
>>> zope.locking.utils.now = hackNow # make code think it is a day later
>>> lock.ended == lock.expiration
True
>>> util.get(demo) is None
True
>>> util.get(demo, util) is util # alternate default works
True
>>> lock.remaining_duration == datetime.timedelta()
True
>>> lock.end()
Traceback (most recent call last):
...
zope.locking.interfaces.EndedError

一旦锁已结束,就无法再更改超时时间。

>>> lock.duration = datetime.timedelta(days=2)
Traceback (most recent call last):
...
zope.locking.interfaces.EndedError

我们将撤销这些修改,并结束锁(在修改完成后,锁将不再结束)。

>>> zope.locking.utils.now = oldNow # undo the hack
>>> lock.end()

请确保注册令牌。创建锁但未注册会使它处于未完全初始化的状态。

>>> lock = tokens.ExclusiveLock(demo, 'john')
>>> lock.started # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.UnregisteredError: ...
>>> lock.ended # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.UnregisteredError: ...

共享锁

共享锁与独占锁非常相似,但在创建时接受一个或多个主体的可迭代对象,并且可以在它们活跃时添加或删除主体。

在这个例子中,请注意TokenUtility register 方法的便捷特性:它还会返回令牌,因此如果需要,可以链式创建、注册和变量赋值。

>>> lock = util.register(tokens.SharedLock(demo, ('john', 'mary')))
>>> ev = events[-1]
>>> verifyObject(interfaces.ITokenStartedEvent, ev)
True
>>> ev.object is lock
True

在这里,具有“john”和“mary” ID的实体已锁定演示对象。返回的令牌实现了ISharedLock,并提供了一组比IExclusiveLock更广泛的特性。接下来的这些操作应该与上面关于ExclusiveLock令牌的讨论非常熟悉。

>>> verifyObject(interfaces.ISharedLock, lock)
True
>>> lock.context is demo
True
>>> lock.__parent__ is demo # important for security
True
>>> lock.utility is util
True
>>> sorted(lock.principal_ids)
['john', 'mary']
>>> lock.ended is None
True
>>> before_creation <= lock.started <= datetime.datetime.now(pytz.utc)
True
>>> lock.expiration is None
True
>>> lock.duration is None
True
>>> lock.remaining_duration is None
True
>>> lock.end()
>>> lock.ended >= lock.started
True

然而,正如之前提到的,共享锁的特性能覆盖独占锁。有两个额外的功能:addremove。这些功能能够添加和删除主体ID,作为锁令牌的共享所有者。

>>> lock = util.register(tokens.SharedLock(demo, ('john',)))
>>> sorted(lock.principal_ids)
['john']
>>> lock.add(('mary',))
>>> sorted(lock.principal_ids)
['john', 'mary']
>>> lock.add(('alice',))
>>> sorted(lock.principal_ids)
['alice', 'john', 'mary']
>>> lock.remove(('john',))
>>> sorted(lock.principal_ids)
['alice', 'mary']
>>> lock.remove(('mary',))
>>> sorted(lock.principal_ids)
['alice']

添加和删除主体将触发适当的事件,正如您所预期的那样。

>>> lock.add(('mary',))
>>> sorted(lock.principal_ids)
['alice', 'mary']
>>> ev = events[-1]
>>> verifyObject(interfaces.IPrincipalsChangedEvent, ev)
True
>>> ev.object is lock
True
>>> sorted(ev.old)
['alice']
>>> lock.remove(('alice',))
>>> sorted(lock.principal_ids)
['mary']
>>> ev = events[-1]
>>> verifyObject(interfaces.IPrincipalsChangedEvent, ev)
True
>>> ev.object is lock
True
>>> sorted(ev.old)
['alice', 'mary']

移除锁中的所有参与者将结束锁,使其变为已结束。

>>> lock.remove(('mary',))
>>> sorted(lock.principal_ids)
[]
>>> lock.ended >= lock.started
True
>>> ev = events[-1]
>>> verifyObject(interfaces.IPrincipalsChangedEvent, ev)
True
>>> ev.object is lock
True
>>> sorted(ev.old)
['mary']
>>> ev = events[-2]
>>> verifyObject(interfaces.ITokenEndedEvent, ev)
True
>>> ev.object is lock
True

正如您所预期的那样,尝试向已结束的锁中添加(或删除!)用户是错误的。

>>> lock.add(('john',))
Traceback (most recent call last):
...
zope.locking.interfaces.EndedError
>>> lock.remove(('john',))
Traceback (most recent call last):
...
zope.locking.interfaces.EndedError

令牌实用工具跟踪共享锁令牌的方式与独占锁令牌相同。以下是代码中的快速总结。

>>> lock = util.register(tokens.SharedLock(demo, ('john', 'mary')))
>>> util.get(demo) is lock
True
>>> list(util.iterForPrincipalId('john')) == [lock]
True
>>> list(util.iterForPrincipalId('mary')) == [lock]
True
>>> list(util) == [lock]
True
>>> util.register(tokens.ExclusiveLock(demo, 'mary'))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...
>>> util.register(tokens.SharedLock(demo, ('mary', 'jane')))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...
>>> util.register(tokens.Freeze(demo))
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.RegistrationError: ...
>>> lock.end()

定时过期与独占锁的工作方式相同。我们在这里不会重复,但请查看此包中的annoying.txt文档,以查看实际的重复测试。

可结束的冻结

可结束的冻结令牌与锁令牌类似,但不会授予任何人的“锁”。

>>> token = util.register(tokens.EndableFreeze(demo))
>>> verifyObject(interfaces.IEndableFreeze, token)
True
>>> ev = events[-1]
>>> verifyObject(interfaces.ITokenStartedEvent, ev)
True
>>> ev.object is token
True
>>> sorted(token.principal_ids)
[]
>>> token.end()

除了可结束的冻结之外,它们与独占锁完全相同。请参阅annoying.txt,以获取复制粘贴测试的综合副本,这些测试重复了独占锁测试。请注意,EndableFreeze永远不会成为主体令牌的可迭代部分:根据定义,冻结与没有任何主体相关联。

冻结

冻结与可结束的冻结类似,但它们不可结束。它们旨在用于系统级操作,这些操作应永久禁用某些更改,例如更改存档对象版本的 内容。

创建它们的方式是相同的...

>>> token = util.register(tokens.Freeze(demo))
>>> verifyObject(interfaces.IFreeze, token)
True
>>> ev = events[-1]
>>> verifyObject(interfaces.ITokenStartedEvent, ev)
True
>>> ev.object is token
True
>>> sorted(token.principal_ids)
[]

但它们不会消失...

>>> token.end()
Traceback (most recent call last):
...
AttributeError: 'Freeze' object has no attribute 'end'

它们也没有过期、持续时间、剩余持续时间或结束日期。它们是永久的,除非您进入数据库来篡改特定于实现的 数据结构。

没有API方法可以结束冻结。我们需要为剩余的演示创建一个新对象,并且此令牌将存在于剩余的示例中。

>>> old_demo = demo
>>> demo = Demo()

用户API,适配器和安全

到目前为止讨论的API对一些常见的锁定用例做出了很少的妥协。以下是迄今为止讨论中尚未满足的一些特定需求。

  • 应该允许或拒绝对象级别的用户创建和注册令牌。

  • 通常,注册可结束令牌比注册永久令牌更容易。

  • 所有用户都应能够解锁或修改他们自己的令牌的一些方面,或从共享令牌中移除他们的参与;但是,应可能限制对用户不拥有的结束令牌的访问(通常称为“打破锁”)。

在Zope 3安全模型中,前两个需求旨在通过ITokenBroker接口及其关联的适配器来解决,最后一个需求旨在通过ITokenHandler及其关联的适配器来解决。

令牌经纪商

令牌经纪人适配器适配一个对象,该对象是代理令牌的对象,并使用该对象作为安全上下文。它们提供了一些有用的方法:locklockSharedfreezeget。TokenBroker期望成为一个受信任的适配器。

lock

lock方法创建并注册一个独占锁。如果没有参数,它将尝试为当前交互中的用户创建它。

当然,没有交互是无法工作的。请注意,我们通过注册实用工具来开始示例。通常,我们需要将实用工具放在站点包中,以便它持久存在,但为了这个演示,我们简化了注册过程。

>>> component.provideUtility(util, provides=interfaces.ITokenUtility)
>>> import zope.interface.interfaces
>>> @interface.implementer(zope.interface.interfaces.IComponentLookup)
... @component.adapter(interface.Interface)
... def siteManager(obj):
...     return component.getGlobalSiteManager()
...
>>> component.provideAdapter(siteManager)
>>> from zope.locking import adapters
>>> component.provideAdapter(adapters.TokenBroker)
>>> broker = interfaces.ITokenBroker(demo)
>>> broker.lock()
Traceback (most recent call last):
...
ValueError
>>> broker.lock('joe')
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError

如果我们与一个参与者建立交互,锁的成功率会更高。

>>> import zope.security.interfaces
>>> @interface.implementer(zope.security.interfaces.IPrincipal)
... class DemoPrincipal(object):
...     def __init__(self, id, title=None, description=None):
...         self.id = id
...         self.title = title
...         self.description = description
...
>>> joe = DemoPrincipal('joe')
>>> import zope.security.management
>>> @interface.implementer(zope.security.interfaces.IParticipation)
... class DemoParticipation(object):
...     def __init__(self, principal):
...         self.principal = principal
...         self.interaction = None
...
>>> zope.security.management.endInteraction()
>>> zope.security.management.newInteraction(DemoParticipation(joe))
>>> token = broker.lock()
>>> interfaces.IExclusiveLock.providedBy(token)
True
>>> token.context is demo
True
>>> token.__parent__ is demo
True
>>> sorted(token.principal_ids)
['joe']
>>> token.started is not None
True
>>> util.get(demo) is token
True
>>> token.end()

您只能指定当前交互中的主体。

>>> token = broker.lock('joe')
>>> sorted(token.principal_ids)
['joe']
>>> token.end()
>>> broker.lock('mary')
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError

此方法可以接受一个持续时间。

>>> token = broker.lock(duration=two)
>>> token.duration == two
True
>>> token.end()

如果交互中有多个主体,则必须指定一个主体(在交互中)。

>>> mary = DemoPrincipal('mary')
>>> participation = DemoParticipation(mary)
>>> zope.security.management.getInteraction().add(participation)
>>> broker.lock()
Traceback (most recent call last):
...
ValueError
>>> broker.lock('susan')
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError
>>> token = broker.lock('joe')
>>> sorted(token.principal_ids)
['joe']
>>> token.end()
>>> token = broker.lock('mary')
>>> sorted(token.principal_ids)
['mary']
>>> token.end()
>>> zope.security.management.endInteraction()

lockShared

lockShared 方法具有类似的特点,但它可以处理多个主体。

没有交互,主体要么找不到,要么不是交互的一部分。

>>> broker.lockShared()
Traceback (most recent call last):
...
ValueError
>>> broker.lockShared(('joe',))
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError

有交互时,主体默认获得锁。

>>> zope.security.management.newInteraction(DemoParticipation(joe))
>>> token = broker.lockShared()
>>> interfaces.ISharedLock.providedBy(token)
True
>>> token.context is demo
True
>>> token.__parent__ is demo
True
>>> sorted(token.principal_ids)
['joe']
>>> token.started is not None
True
>>> util.get(demo) is token
True
>>> token.end()

您只能指定当前交互中的主体。

>>> token = broker.lockShared(('joe',))
>>> sorted(token.principal_ids)
['joe']
>>> token.end()
>>> broker.lockShared(('mary',))
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError

此方法可以接受一个持续时间。

>>> token = broker.lockShared(duration=two)
>>> token.duration == two
True
>>> token.end()

如果交互中有多个主体,则所有主体都包括在内,除非某些主体被单独指定。

>>> participation = DemoParticipation(mary)
>>> zope.security.management.getInteraction().add(participation)
>>> token = broker.lockShared()
>>> sorted(token.principal_ids)
['joe', 'mary']
>>> token.end()
>>> token = broker.lockShared(('joe',))
>>> sorted(token.principal_ids)
['joe']
>>> token.end()
>>> token = broker.lockShared(('mary',))
>>> sorted(token.principal_ids)
['mary']
>>> token.end()
>>> zope.security.management.endInteraction()

freeze

freeze 方法允许用户创建一个可终止的冻结。它对交互没有要求。应从安全角度小心保护。

>>> token = broker.freeze()
>>> interfaces.IEndableFreeze.providedBy(token)
True
>>> token.context is demo
True
>>> token.__parent__ is demo
True
>>> sorted(token.principal_ids)
[]
>>> token.started is not None
True
>>> util.get(demo) is token
True
>>> token.end()

此方法可以接受一个持续时间。

>>> token = broker.freeze(duration=two)
>>> token.duration == two
True
>>> token.end()

get

get 方法与令牌实用工具的 get 方法完全等价:它返回对象的当前活动令牌,或 None。这对于受保护的代码很有用,因为实用工具通常不获取安全断言,而此方法可以从对象获取其安全断言,这通常是正确的位置。

再次强调,TokenBroker 确实体现了某些策略;如果这不是您应用程序的好策略,请构建自己的接口和适配器。

令牌处理器

TokenHandlers 对于具有一个或多个主体的可终止令牌很有用——也就是说,锁,但不包括冻结。它们旨在以比通常的令牌方法和属性更低的外部安全权限进行保护,然后根据当前交互执行自己的检查。它们在很大程度上是策略,其他方法可能也很有用。它们旨在作为可信适配器进行注册。

因此,对于排他性锁和共享锁,我们有了令牌处理器。通常,令牌处理器提供了与其对应的令牌相同的所有功能,以下是一些额外的约束和能力:

  • 过期时间持续时间剩余持续时间 只能在当前交互中的所有主体都是包装令牌的所有者的情况下设置;

  • 释放 如果当前交互中的所有主体都是包装令牌的所有者,则从交互中删除一些或所有主体。

请注意,end 不受影响:这实际上是“打破锁”,而 release 实际上是“解锁”。权限应相应设置。

共享锁处理器在其部分中讨论了两个额外的方法。

排他锁处理器

基于上述一般约束,排他性锁处理器通常仅在操作与仅锁所有者的交互中时才允许访问其特殊功能。

>>> zope.security.management.newInteraction(DemoParticipation(joe))
>>> component.provideAdapter(adapters.ExclusiveLockHandler)
>>> lock = broker.lock()
>>> handler = interfaces.IExclusiveLockHandler(lock)
>>> verifyObject(interfaces.IExclusiveLockHandler, handler)
True
>>> handler.__parent__ is lock
True
>>> handler.expiration is None
True
>>> handler.duration = two
>>> lock.duration == two
True
>>> handler.expiration = handler.started + three
>>> lock.expiration == handler.started + three
True
>>> handler.remaining_duration = two
>>> lock.remaining_duration <= two
True
>>> handler.release()
>>> handler.ended >= handler.started
True
>>> lock.ended >= lock.started
True
>>> lock = util.register(tokens.ExclusiveLock(demo, 'mary'))
>>> handler = interfaces.ITokenHandler(lock) # for joe's interaction still
>>> handler.duration = two # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.expiration = handler.started + three # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.remaining_duration = two # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.release() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> lock.end()

共享锁处理器

共享锁处理器让任何令牌的所有者设置过期时间、持续时间和剩余持续时间值。这是一种“让路”策略,它依赖于社会互动以确保所有参与者都代表他们想要的。可以在其他适配器中编写其他策略。

>>> component.provideAdapter(adapters.SharedLockHandler)
>>> lock = util.register(tokens.SharedLock(demo, ('joe', 'mary')))
>>> handler = interfaces.ITokenHandler(lock) # for joe's interaction still
>>> verifyObject(interfaces.ISharedLockHandler, handler)
True
>>> handler.__parent__ is lock
True
>>> handler.expiration is None
True
>>> handler.duration = two
>>> lock.duration == two
True
>>> handler.expiration = handler.started + three
>>> lock.expiration == handler.started + three
True
>>> handler.remaining_duration = two
>>> lock.remaining_duration <= two
True
>>> sorted(handler.principal_ids)
['joe', 'mary']
>>> handler.release()
>>> sorted(handler.principal_ids)
['mary']
>>> handler.duration = two # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.expiration = handler.started + three # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.remaining_duration = two # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> handler.release() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...

共享锁处理器向标准处理器添加了两个额外的方法:joinadd。它们执行类似的工作,但分开以允许为每个单独的安全设置。join 方法允许当前交互中的某些或所有主体加入。

>>> handler.join()
>>> sorted(handler.principal_ids)
['joe', 'mary']
>>> handler.join(('susan',))
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError

add 方法允许将任何主体 ID 添加到锁中,但当前交互中的所有主体都必须是锁的一部分。

>>> handler.add(('susan',))
>>> sorted(handler.principal_ids)
['joe', 'mary', 'susan']
>>> handler.release()
>>> handler.add('jake') # doctest: +ELLIPSIS
Traceback (most recent call last):
...
zope.locking.interfaces.ParticipationError: ...
>>> lock.end()
>>> zope.security.management.endInteraction()

警告

  • 令牌实用工具会在可能的情况下为对象注册令牌。它不会检查它是否确实是给定对象的本地令牌实用工具。这应由令牌实用工具的客户端来安排,如果需要,可以通过外部验证。

  • 令牌作为BTree中的键存储,因此必须是可排序的(即,它们必须实现 __cmp__)。

预期安全配置

在Zope 3中,实用工具通常是未受保护的——或者更准确地说,没有安全断言,并且没有安全代理的使用——令牌实用工具期望如此。因此,经纪人对象和处理对象应预期为视图代码使用的对象,并且与安全代理相关联。所有这些都应该具有适当的 __parent__ 属性值。修改令牌的能力——例如,endaddremove 方法——应通过类似于“zope.Security”的管理员类型权限进行保护。设置令牌的超时属性应以相同的方式进行保护。设置处理器的属性可以具有更宽松的设置,因为它们根据锁成员资格自己计算安全性。

在适配器上,end 方法应使用相同或类似的权限进行保护。调用锁定和锁定共享等方法应使用类似“zope.ManageContent”的权限。获取属性应为“zope.View”或“zope.Public”,解锁和设置超时,因为它们已经受到保护以确保主体是锁的成员,可能可以“zope.Public”。

这些设置可以通过相对容易的方式滥用以创建不安全的系统——例如,如果用户可以获取对另一个主体的 IPrincipalLockable 的适配器——但这是一个合理的起点。

>>> broker.__parent__ is demo
True
>>> handler.__parent__ is lock
True

随想

由于设计的影响,可能会同时使用多个锁定实用工具,以管理对象的不同方面;然而,这本身可能永远不会有用。

更改

2.1.0 (2020-04-15)

  • 修复 ObjectEvent 的弃用警告。

  • 添加对 Python 3.7 和 3.8 的支持。

  • 放弃对 Python 3.3 和 3.4 的支持。

2.0.0 (2018-01-23)

  • Python 3 兼容性。

  • 注意:浏览器视图和相关代码已删除。现在需要在应用程序级代码中提供这些。

  • 打包 zcml 文件。

  • 更新依赖关系。

  • 从 svn.zope.org 恢复。

1.2.2 (2011-01-31)

  • 合并重复的进化代码。

  • 将生成配置拆分为自己的 zcml 文件。

1.2.1 (2010-01-20)

  • 错误修复:1.2 中添加的生成没有正确清理过期的令牌,并且可能使令牌实用工具处于不一致的状态。

1.2 (2009-11-23)

  • 错误修复:令牌以阻止在实用工具的 _principal_ids 映射中正确清理的方式存储。将 zope.locking.tokens.Token 制造为可排序的以修复此问题,因为令牌作为 BTree 中的键存储。

  • 添加 zope.app.generations Schema Manager 以清理由于此错误而留下的任何残留令牌。无法通过组件注册访问的令牌实用工具可以手动使用 zope.locking.generations.fix_token_utility 进行清理。

  • TokenUtility 的 register 方法现在将在令牌提供 IPersistent 时将其添加到实用工具的数据库连接中。

  • 清理测试和文档,并将一些常用代码移动到 testing.py。

  • 修复一些缺失的导入。

1.1

(Zope 3.4 的系列;egg)

1.1b

  • 转换为使用 eggs

1.0

(Zope 3.3 的系列;不依赖于 Zope eggs)

1.0b

初始非开发版本

项目详情


下载文件

下载您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分发

zope.locking-2.1.0.tar.gz (44.9 kB 查看哈希值)

上传时间 源码

由以下支持