velocity-python 0.4.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- velocity/__init__.py +1 -1
- velocity/workflow/access.py +8 -1
- velocity/workflow/engine.py +56 -5
- velocity/workflow/schema.py +4 -0
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/METADATA +1 -1
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/RECORD +10 -10
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/WHEEL +0 -0
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/entry_points.txt +0 -0
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.4.0.dist-info → velocity_python-0.4.1.dist-info}/top_level.txt +0 -0
velocity/__init__.py
CHANGED
velocity/workflow/access.py
CHANGED
|
@@ -288,8 +288,15 @@ def act_relations(
|
|
|
288
288
|
if kind == "hold":
|
|
289
289
|
hold = tx.table(engine.HOLD).find({"sys_id": ident})
|
|
290
290
|
if hold is not None and hold["docket_id"] == docket["sys_id"]:
|
|
291
|
+
# the relation only; the engine decides the hold's own
|
|
292
|
+
# release_permission with the actor's bound authz
|
|
291
293
|
if engine.hold_owner_of(
|
|
292
|
-
actor,
|
|
294
|
+
actor,
|
|
295
|
+
route,
|
|
296
|
+
dict(hold.to_dict()),
|
|
297
|
+
docket.get("entity"),
|
|
298
|
+
docket=docket,
|
|
299
|
+
check_permission=False,
|
|
293
300
|
):
|
|
294
301
|
out.add(HOLD_OWNER)
|
|
295
302
|
return frozenset(out)
|
velocity/workflow/engine.py
CHANGED
|
@@ -891,10 +891,15 @@ def may_release(actor: Actor, route: Route, hold, docket, effect) -> bool:
|
|
|
891
891
|
return False
|
|
892
892
|
if actor.kind != "person" or not in_scope(actor, docket.get("entity")):
|
|
893
893
|
return False
|
|
894
|
+
held = hold.get("release_permission")
|
|
895
|
+
if held and not actor.may(HOLD_RELEASE, held, docket):
|
|
896
|
+
return False # the hold's own permission binds every way out
|
|
894
897
|
permission = effect.get("release_permission")
|
|
895
898
|
if permission:
|
|
896
899
|
return actor.may(HOLD_RELEASE, permission, docket)
|
|
897
|
-
return hold_owner_of(
|
|
900
|
+
return hold_owner_of(
|
|
901
|
+
actor, route, hold, docket.get("entity"), docket=docket, direct=False
|
|
902
|
+
)
|
|
898
903
|
|
|
899
904
|
|
|
900
905
|
def _release_effects(item: Pass):
|
|
@@ -1659,16 +1664,43 @@ def problem_owner_of(actor: Actor, route: Route, position) -> bool:
|
|
|
1659
1664
|
)
|
|
1660
1665
|
|
|
1661
1666
|
|
|
1662
|
-
|
|
1667
|
+
RELEASE_ANY, RELEASE_VIA_EFFECT = "any", "effect"
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
def hold_owner_of(
|
|
1671
|
+
actor: Actor,
|
|
1672
|
+
route: Route,
|
|
1673
|
+
hold,
|
|
1674
|
+
entity,
|
|
1675
|
+
*,
|
|
1676
|
+
docket=None,
|
|
1677
|
+
direct=True,
|
|
1678
|
+
check_permission=True,
|
|
1679
|
+
) -> bool:
|
|
1663
1680
|
"""Who may release a manual hold on a docket of ``entity``: its owner
|
|
1664
|
-
role, its placer, a route owner — inside their
|
|
1681
|
+
role, its placer, a route owner, an authorized override — inside their
|
|
1682
|
+
entities, and subject to the hold's own release controls:
|
|
1683
|
+
|
|
1684
|
+
* ``release_via="effect"``: never released directly (the
|
|
1685
|
+
``hold:<id>:release`` action, ``direct=True``), only by a pass effect;
|
|
1686
|
+
* ``release_permission``: whoever releases it must also be allowed that
|
|
1687
|
+
permission through authz (:data:`HOLD_RELEASE`, with the docket's
|
|
1688
|
+
entity scope) — a route owner or an override included. Needs
|
|
1689
|
+
``docket``; ``check_permission=False`` is only for computing an authz
|
|
1690
|
+
relation, the engine then decides with the actor's own ``allows``."""
|
|
1665
1691
|
if actor.kind != "person" or str(hold.get("release_rule") or "") not in (
|
|
1666
1692
|
"",
|
|
1667
1693
|
"manual",
|
|
1668
1694
|
):
|
|
1669
1695
|
return False
|
|
1696
|
+
if direct and (hold.get("release_via") or RELEASE_ANY) == RELEASE_VIA_EFFECT:
|
|
1697
|
+
return False
|
|
1670
1698
|
if not in_scope(actor, entity):
|
|
1671
1699
|
return False
|
|
1700
|
+
permission = hold.get("release_permission")
|
|
1701
|
+
if permission and check_permission:
|
|
1702
|
+
if docket is None or not actor.may(HOLD_RELEASE, permission, docket):
|
|
1703
|
+
return False
|
|
1672
1704
|
if actor.override:
|
|
1673
1705
|
return True
|
|
1674
1706
|
return (
|
|
@@ -1777,7 +1809,7 @@ def actions_for(tx, docket, route: Route, actor: Actor, *, positions=None, now=N
|
|
|
1777
1809
|
}
|
|
1778
1810
|
)
|
|
1779
1811
|
for hold in holds_of(tx, docket["sys_id"]):
|
|
1780
|
-
if hold_owner_of(actor, route, hold, docket.get("entity")):
|
|
1812
|
+
if hold_owner_of(actor, route, hold, docket.get("entity"), docket=docket):
|
|
1781
1813
|
out.append(
|
|
1782
1814
|
_action(
|
|
1783
1815
|
f"hold:{hold['sys_id']}:release",
|
|
@@ -2217,11 +2249,28 @@ def place_hold(
|
|
|
2217
2249
|
blocks=None,
|
|
2218
2250
|
releases_when=None,
|
|
2219
2251
|
label=None,
|
|
2252
|
+
release_permission=None,
|
|
2253
|
+
release_via=RELEASE_ANY,
|
|
2220
2254
|
now=None,
|
|
2221
2255
|
) -> int:
|
|
2222
2256
|
"""Place a manual hold; returns its id. ``blocks``: the pass codes it
|
|
2223
|
-
blocks (default every forward, branch and rejoin pass).
|
|
2257
|
+
blocks (default every forward, branch and rejoin pass).
|
|
2258
|
+
|
|
2259
|
+
Release controls: ``release_permission`` — whoever releases it (owner,
|
|
2260
|
+
route owner, override, or a pass effect) must be allowed that permission
|
|
2261
|
+
through authz (``workflow.hold.release``); ``release_via="effect"`` —
|
|
2262
|
+
only a pass's ``release_hold`` effect releases it, so its pass's guards
|
|
2263
|
+
always apply: the direct ``hold:<id>:release`` action is never offered
|
|
2264
|
+
and is refused if sent."""
|
|
2224
2265
|
hooks.check_code(kind, "hold kind")
|
|
2266
|
+
if release_via not in (RELEASE_ANY, RELEASE_VIA_EFFECT):
|
|
2267
|
+
raise WorkflowError(
|
|
2268
|
+
f"release_via must be {RELEASE_ANY!r} or {RELEASE_VIA_EFFECT!r}"
|
|
2269
|
+
)
|
|
2270
|
+
if release_permission is not None and (
|
|
2271
|
+
not isinstance(release_permission, str) or not release_permission.strip()
|
|
2272
|
+
):
|
|
2273
|
+
raise WorkflowError("release_permission must be a permission name")
|
|
2225
2274
|
ctx = _Ctx(tx, now)
|
|
2226
2275
|
docket = _docket(tx, docket_id, lock=True)
|
|
2227
2276
|
row = tx.table(HOLD).new(
|
|
@@ -2236,6 +2285,8 @@ def place_hold(
|
|
|
2236
2285
|
"release_rule": "manual",
|
|
2237
2286
|
"releases_when": releases_when,
|
|
2238
2287
|
"blocks": list(blocks) if blocks else None,
|
|
2288
|
+
"release_permission": release_permission,
|
|
2289
|
+
"release_via": release_via,
|
|
2239
2290
|
}
|
|
2240
2291
|
)
|
|
2241
2292
|
_mark(ctx, docket, actor, "hold", outcome=kind, note=str(reason or kind))
|
velocity/workflow/schema.py
CHANGED
|
@@ -317,6 +317,10 @@ SCHEMA = {
|
|
|
317
317
|
"released_by": "TEXT",
|
|
318
318
|
"released_at": TS,
|
|
319
319
|
"blocks": "JSONB",
|
|
320
|
+
# release controls: a permission any release needs, and
|
|
321
|
+
# "effect" = only a pass effect may release it (no direct action)
|
|
322
|
+
"release_permission": "TEXT",
|
|
323
|
+
"release_via": "TEXT",
|
|
320
324
|
"link_id": "BIGINT",
|
|
321
325
|
"rule_pack": "TEXT",
|
|
322
326
|
"rule_version": "INTEGER",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
velocity/__init__.py,sha256=
|
|
1
|
+
velocity/__init__.py,sha256=cKKghorIQ6KmDenkl1684EwlR7g6o3H-aBxwT9VLk5o,362
|
|
2
2
|
velocity/cache.py,sha256=pthvw7gDVEXRzkTcQtA5YVf4Q1IxAamWsba4UReT0RE,8933
|
|
3
3
|
velocity/hooks.py,sha256=jY_qtAapNHvptmryFSD-Zc7RvLhV6e5nJEH3x2xTgCw,20575
|
|
4
4
|
velocity/logging.py,sha256=MAsZap1cDSdOBNRc_9ezQxup5dHOS0ARxU9NI_-UK3E,4037
|
|
@@ -239,18 +239,18 @@ velocity/rules/storage.py,sha256=dCWhN6g5_kQCTWy66CgpyNmIwrTY1VsVsMPXKoaiO6U,130
|
|
|
239
239
|
velocity/testing/__init__.py,sha256=WtYMDHgCLxOq6mlemcOYV5Y9HqF_2QQgSfuAjFRg69Q,731
|
|
240
240
|
velocity/testing/fake_db.py,sha256=phgdulk5owf3noUDpEfI7yFsVcKxRGZ82f2BJPYnaiU,53226
|
|
241
241
|
velocity/workflow/__init__.py,sha256=yVB00hLDhKmQnUGIwEHtwUcwk9MS79M3S0l5SGI9icc,3704
|
|
242
|
-
velocity/workflow/access.py,sha256=
|
|
242
|
+
velocity/workflow/access.py,sha256=OPmcFTKvGP6mQnQTzPZjbPAi6TfrYUu1e4UkI8EqWy4,12668
|
|
243
243
|
velocity/workflow/contract.py,sha256=PoWfQ8lseAZU0WLJiNOvgyJ2ZDDq7e6Ji2r497oap6w,38904
|
|
244
244
|
velocity/workflow/definitions.py,sha256=2UXxd7dQLdBi8X5H0_W1Qo4cmXJVlDpzLhpBdsbbUqs,31979
|
|
245
|
-
velocity/workflow/engine.py,sha256=
|
|
245
|
+
velocity/workflow/engine.py,sha256=XGnEfBIGKAWMgq5pJsbSoYy86vYj2yaiLqRZq7v-lhg,123922
|
|
246
246
|
velocity/workflow/hooks.py,sha256=0lDMuAE-OwL1_M8wnylU46zBgOooJomVErPUaqdfR1c,14995
|
|
247
247
|
velocity/workflow/notices.py,sha256=hTCVKyZn7ScJNPSgPYaa14QTRxKH5VHwAgNnV6xHW8E,13825
|
|
248
248
|
velocity/workflow/outbox.py,sha256=R-DmdeVETYZc0u__OXxhKyeLgM1Cwq555-RqAFTW7h0,9412
|
|
249
249
|
velocity/workflow/points.py,sha256=g_vQ3cdHdx_JloVCS5dVjo_jJoSolO92K5p3suDzxlE,10654
|
|
250
|
-
velocity/workflow/schema.py,sha256=
|
|
251
|
-
velocity_python-0.4.
|
|
252
|
-
velocity_python-0.4.
|
|
253
|
-
velocity_python-0.4.
|
|
254
|
-
velocity_python-0.4.
|
|
255
|
-
velocity_python-0.4.
|
|
256
|
-
velocity_python-0.4.
|
|
250
|
+
velocity/workflow/schema.py,sha256=AY1VkRFRuddL9egJHh8-cdrh8-tDsg2MLDs0ihEKskc,20461
|
|
251
|
+
velocity_python-0.4.1.dist-info/licenses/LICENSE,sha256=aoN245GG8s9oRUU89KNiGTU4_4OtnNmVi4hQeChg6rM,1076
|
|
252
|
+
velocity_python-0.4.1.dist-info/METADATA,sha256=1P5cdnKxBa7go3LyXCa-_ywsXyoTOz5W1woGwpeo5Og,17820
|
|
253
|
+
velocity_python-0.4.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
254
|
+
velocity_python-0.4.1.dist-info/entry_points.txt,sha256=nXUKJHkOGQCSPoagdmMNd_qycrle1LVU-GhYknLGM1M,56
|
|
255
|
+
velocity_python-0.4.1.dist-info/top_level.txt,sha256=JW2vJPmodgdgSz7H6yoZvnxF8S3fTMIv-YJWCT1sNW0,9
|
|
256
|
+
velocity_python-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|