mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Implemented actor_from_request with tests, refs #699
Also added datasette argument to permission_allowed hook
This commit is contained in:
parent
060a56735c
commit
461c82838d
6 changed files with 80 additions and 2 deletions
|
|
@ -798,7 +798,18 @@ class DatasetteRouter(AsgiRouter):
|
|||
and scope.get("scheme") != "https"
|
||||
):
|
||||
scope = dict(scope, scheme="https")
|
||||
return await super().route_path(scope, receive, send, path)
|
||||
# Handle authentication
|
||||
actor = None
|
||||
for actor in pm.hook.actor_from_request(
|
||||
datasette=self.ds, request=Request(scope, receive)
|
||||
):
|
||||
if callable(actor):
|
||||
actor = actor()
|
||||
if asyncio.iscoroutine(actor):
|
||||
actor = await actor
|
||||
if actor:
|
||||
break
|
||||
return await super().route_path(dict(scope, actor=actor), receive, send, path)
|
||||
|
||||
async def handle_404(self, scope, receive, send, exception=None):
|
||||
# If URL has a trailing slash, redirect to URL without it
|
||||
|
|
|
|||
|
|
@ -66,5 +66,5 @@ def actor_from_request(datasette, request):
|
|||
|
||||
|
||||
@hookspec
|
||||
def permission_allowed(actor, action, resource_type, resource_identifier):
|
||||
def permission_allowed(datasette, actor, action, resource_type, resource_identifier):
|
||||
"Check if actor is allowed to perfom this action - return True, False or None"
|
||||
|
|
|
|||
|
|
@ -957,6 +957,29 @@ This is part of Datasette's authentication and permissions system. The function
|
|||
|
||||
If it cannot authenticate an actor, it should return ``None``. Otherwise it should return a dictionary representing that actor.
|
||||
|
||||
Instead of returning a dictionary, this function can return an awaitable function which itself returns either ``None`` or a dictionary. This is useful for authentication functions that need to make a database query - for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from datasette import hookimpl
|
||||
|
||||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
async def inner():
|
||||
token = request.args.get("_token")
|
||||
if not token:
|
||||
return None
|
||||
# Look up ?_token=xxx in sessions table
|
||||
result = await datasette.get_database().execute(
|
||||
"select count(*) from sessions where token = ?", [token]
|
||||
)
|
||||
if result.first()[0]:
|
||||
return {"token": token}
|
||||
else:
|
||||
return None
|
||||
|
||||
return inner
|
||||
|
||||
.. _plugin_permission_allowed:
|
||||
|
||||
permission_allowed(datasette, actor, action, resource_type, resource_identifier)
|
||||
|
|
|
|||
|
|
@ -126,3 +126,11 @@ class DummyFacet(Facet):
|
|||
facet_results = {}
|
||||
facets_timed_out = []
|
||||
return facet_results, facets_timed_out
|
||||
|
||||
|
||||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
if request.args.get("_bot"):
|
||||
return {"id": "bot"}
|
||||
else:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -95,3 +95,15 @@ def asgi_wrapper(datasette):
|
|||
return add_x_databases_header
|
||||
|
||||
return wrap_with_databases_header
|
||||
|
||||
|
||||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
async def inner():
|
||||
if request.args.get("_bot2"):
|
||||
result = await datasette.get_database().execute("select 1 + 1")
|
||||
return {"id": "bot2", "1+1": result.first()[0]}
|
||||
else:
|
||||
return None
|
||||
|
||||
return inner
|
||||
|
|
|
|||
|
|
@ -503,3 +503,27 @@ def test_register_facet_classes(app_client):
|
|||
"toggle_url": "http://localhost/fixtures/compound_three_primary_keys.json?_dummy_facet=1&_facet=pk3",
|
||||
},
|
||||
] == data["suggested_facets"]
|
||||
|
||||
|
||||
def test_actor_from_request(app_client):
|
||||
app_client.get("/")
|
||||
# Should have no actor
|
||||
assert None == app_client.ds._last_request.scope["actor"]
|
||||
app_client.get("/?_bot=1")
|
||||
# Should have bot actor
|
||||
assert {"id": "bot"} == app_client.ds._last_request.scope["actor"]
|
||||
|
||||
|
||||
def test_actor_from_request_async(app_client):
|
||||
app_client.get("/")
|
||||
# Should have no actor
|
||||
assert None == app_client.ds._last_request.scope["actor"]
|
||||
app_client.get("/?_bot2=1")
|
||||
# Should have bot2 actor
|
||||
assert {"id": "bot2", "1+1": 2} == app_client.ds._last_request.scope["actor"]
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_permission_allowed(app_client):
|
||||
# TODO
|
||||
assert False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue