mirror of
https://github.com/simonw/datasette.git
synced 2026-10-27 16:46:54 +01:00
Fix permission rule source attribution to use pluggy call order (#2978)
Pair each hook result with reversed(get_hookimpls()): pluggy runs implementations in reverse registration order and appends results in that order. Adds regression tests.
This commit is contained in:
parent
cec5e6b2ef
commit
4a2ef57f5e
2 changed files with 75 additions and 1 deletions
70
tests/test_permission_source_attribution.py
Normal file
70
tests/test_permission_source_attribution.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""Permission rules are attributed to the plugin that returned them (#2978).
|
||||
|
||||
``gather_permission_sql_from_hooks()`` used to pair each hook result with a
|
||||
hook implementation by index into ``get_hookimpls()``, but pluggy calls
|
||||
implementations in reverse registration order — so every rule whose plugin
|
||||
did not set ``PermissionSQL.source`` explicitly was attributed to whichever
|
||||
plugin happened to sit at that index.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from datasette import hookimpl
|
||||
from datasette.app import Datasette
|
||||
from datasette.permissions import PermissionSQL
|
||||
from datasette.utils.permissions import gather_permission_sql_from_hooks
|
||||
|
||||
|
||||
class AlphaPlugin:
|
||||
@hookimpl
|
||||
def permission_resources_sql(self, datasette, actor, action):
|
||||
if action == "view-table":
|
||||
return PermissionSQL(sql="select 'a' as parent, 'x' as child, 1 as allow, 'from-alpha' as reason")
|
||||
|
||||
|
||||
class BetaPlugin:
|
||||
@hookimpl
|
||||
def permission_resources_sql(self, datasette, actor, action):
|
||||
if action == "view-table":
|
||||
return PermissionSQL(sql="select 'b' as parent, 'y' as child, 1 as allow, 'from-beta' as reason")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_permission_sql_source_names_the_calling_plugin():
|
||||
ds = Datasette(memory=True)
|
||||
ds.pm.register(AlphaPlugin(), name="alpha")
|
||||
ds.pm.register(BetaPlugin(), name="beta")
|
||||
try:
|
||||
await ds.invoke_startup()
|
||||
sqls = await gather_permission_sql_from_hooks(datasette=ds, actor={"id": "alice"}, action="view-table")
|
||||
finally:
|
||||
ds.pm.unregister(name="beta")
|
||||
ds.pm.unregister(name="alpha")
|
||||
ds.close()
|
||||
by_plugin = {}
|
||||
for permission_sql in sqls:
|
||||
if "'a' as parent" in (permission_sql.sql or ""):
|
||||
by_plugin["alpha"] = permission_sql.source
|
||||
elif "'b' as parent" in (permission_sql.sql or ""):
|
||||
by_plugin["beta"] = permission_sql.source
|
||||
assert by_plugin == {"alpha": "alpha", "beta": "beta"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_explicit_permission_sql_source_is_preserved():
|
||||
class ExplicitPlugin:
|
||||
@hookimpl
|
||||
def permission_resources_sql(self, datasette, actor, action):
|
||||
if action == "view-table":
|
||||
return PermissionSQL(sql="select 'e' as parent, 'z' as child, 1 as allow, 'explicit' as reason", source="my-source")
|
||||
|
||||
ds = Datasette(memory=True)
|
||||
ds.pm.register(ExplicitPlugin(), name="explicit")
|
||||
try:
|
||||
await ds.invoke_startup()
|
||||
sqls = await gather_permission_sql_from_hooks(datasette=ds, actor={"id": "alice"}, action="view-table")
|
||||
finally:
|
||||
ds.pm.unregister(name="explicit")
|
||||
ds.close()
|
||||
sources = [permission_sql.source for permission_sql in sqls if "'e' as parent" in (permission_sql.sql or "")]
|
||||
assert sources == ["my-source"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue