mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
datasette.set_actor_cookie() and datasette.delete_actor_cookie(), closes #1690
This commit is contained in:
parent
37873e02b0
commit
308c243cfd
4 changed files with 36 additions and 32 deletions
|
|
@ -67,6 +67,7 @@ from .utils import (
|
||||||
StartupError,
|
StartupError,
|
||||||
async_call_with_supported_arguments,
|
async_call_with_supported_arguments,
|
||||||
await_me_maybe,
|
await_me_maybe,
|
||||||
|
baseconv,
|
||||||
call_with_supported_arguments,
|
call_with_supported_arguments,
|
||||||
detect_json1,
|
detect_json1,
|
||||||
display_actor,
|
display_actor,
|
||||||
|
|
@ -1430,6 +1431,18 @@ class Datasette:
|
||||||
|
|
||||||
return await template.render_async(template_context)
|
return await template.render_async(template_context)
|
||||||
|
|
||||||
|
def set_actor_cookie(
|
||||||
|
self, response: Response, actor: dict, expire_after: Optional[int] = None
|
||||||
|
):
|
||||||
|
data = {"a": actor}
|
||||||
|
if expire_after:
|
||||||
|
expires_at = int(time.time()) + (24 * 60 * 60)
|
||||||
|
data["e"] = baseconv.base62.encode(expires_at)
|
||||||
|
response.set_cookie("ds_actor", self.sign(data, "actor"))
|
||||||
|
|
||||||
|
def delete_actor_cookie(self, response: Response):
|
||||||
|
response.set_cookie("ds_actor", "", expires=0, max_age=0)
|
||||||
|
|
||||||
async def _asset_urls(self, key, template, context, request, view_name):
|
async def _asset_urls(self, key, template, context, request, view_name):
|
||||||
# Flatten list-of-lists from plugins:
|
# Flatten list-of-lists from plugins:
|
||||||
seen_urls = set()
|
seen_urls = set()
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class AuthTokenView(BaseView):
|
||||||
self.ds._root_token = None
|
self.ds._root_token = None
|
||||||
response = Response.redirect(self.ds.urls.instance())
|
response = Response.redirect(self.ds.urls.instance())
|
||||||
root_actor = {"id": "root"}
|
root_actor = {"id": "root"}
|
||||||
response.set_cookie("ds_actor", self.ds.sign({"a": root_actor}, "actor"))
|
self.ds.set_actor_cookie(response, root_actor)
|
||||||
await self.ds.track_event(LoginEvent(actor=root_actor))
|
await self.ds.track_event(LoginEvent(actor=root_actor))
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
|
|
@ -107,7 +107,7 @@ class LogoutView(BaseView):
|
||||||
|
|
||||||
async def post(self, request):
|
async def post(self, request):
|
||||||
response = Response.redirect(self.ds.urls.instance())
|
response = Response.redirect(self.ds.urls.instance())
|
||||||
response.set_cookie("ds_actor", "", expires=0, max_age=0)
|
self.ds.delete_actor_cookie(response)
|
||||||
self.ds.add_message(request, "You are now logged out", self.ds.WARNING)
|
self.ds.add_message(request, "You are now logged out", self.ds.WARNING)
|
||||||
await self.ds.track_event(LogoutEvent(actor=request.actor))
|
await self.ds.track_event(LogoutEvent(actor=request.actor))
|
||||||
return response
|
return response
|
||||||
|
|
|
||||||
|
|
@ -1062,19 +1062,25 @@ Authentication plugins can set signed ``ds_actor`` cookies themselves like so:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
response = Response.redirect("/")
|
response = Response.redirect("/")
|
||||||
response.set_cookie(
|
datasette.set_actor_cookie(response, {"id": "cleopaws"})
|
||||||
"ds_actor",
|
|
||||||
datasette.sign({"a": {"id": "cleopaws"}}, "actor"),
|
|
||||||
)
|
|
||||||
|
|
||||||
Note that you need to pass ``"actor"`` as the namespace to :ref:`datasette_sign`.
|
The shape of data encoded in the cookie is as follows:
|
||||||
|
|
||||||
The shape of data encoded in the cookie is as follows::
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
"a": {... actor ...}
|
"a": {
|
||||||
|
"id": "cleopaws"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
To implement logout in a plugin, use the ``delete_actor_cookie()`` method:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
response = Response.redirect("/")
|
||||||
|
datasette.delete_actor_cookie(response)
|
||||||
|
|
||||||
.. _authentication_ds_actor_expiry:
|
.. _authentication_ds_actor_expiry:
|
||||||
|
|
||||||
Including an expiry time
|
Including an expiry time
|
||||||
|
|
@ -1082,25 +1088,13 @@ Including an expiry time
|
||||||
|
|
||||||
``ds_actor`` cookies can optionally include a signed expiry timestamp, after which the cookies will no longer be valid. Authentication plugins may chose to use this mechanism to limit the lifetime of the cookie. For example, if a plugin implements single-sign-on against another source it may decide to set short-lived cookies so that if the user is removed from the SSO system their existing Datasette cookies will stop working shortly afterwards.
|
``ds_actor`` cookies can optionally include a signed expiry timestamp, after which the cookies will no longer be valid. Authentication plugins may chose to use this mechanism to limit the lifetime of the cookie. For example, if a plugin implements single-sign-on against another source it may decide to set short-lived cookies so that if the user is removed from the SSO system their existing Datasette cookies will stop working shortly afterwards.
|
||||||
|
|
||||||
To include an expiry, add a ``"e"`` key to the cookie value containing a base62-encoded integer representing the timestamp when the cookie should expire. For example, here's how to set a cookie that expires after 24 hours:
|
To include an expiry pass ``expire_after=`` to ``datasette.set_actor_cookie()`` with a number of seconds. For example, to expire in 24 hours:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import time
|
|
||||||
from datasette.utils import baseconv
|
|
||||||
|
|
||||||
expires_at = int(time.time()) + (24 * 60 * 60)
|
|
||||||
|
|
||||||
response = Response.redirect("/")
|
response = Response.redirect("/")
|
||||||
response.set_cookie(
|
datasette.set_actor_cookie(
|
||||||
"ds_actor",
|
response, {"id": "cleopaws"}, expire_after=60 * 60 * 24
|
||||||
datasette.sign(
|
|
||||||
{
|
|
||||||
"a": {"id": "cleopaws"},
|
|
||||||
"e": baseconv.base62.encode(expires_at),
|
|
||||||
},
|
|
||||||
"actor",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
The resulting cookie will encode data that looks something like this:
|
The resulting cookie will encode data that looks something like this:
|
||||||
|
|
@ -1108,13 +1102,12 @@ The resulting cookie will encode data that looks something like this:
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
{
|
{
|
||||||
"a": {
|
"a": {
|
||||||
"id": "cleopaws"
|
"id": "cleopaws"
|
||||||
},
|
},
|
||||||
"e": "1jjSji"
|
"e": "1jjSji"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.. _LogoutView:
|
.. _LogoutView:
|
||||||
|
|
||||||
The /-/logout page
|
The /-/logout page
|
||||||
|
|
|
||||||
|
|
@ -279,9 +279,7 @@ def register_routes():
|
||||||
# Mainly for the latest.datasette.io demo
|
# Mainly for the latest.datasette.io demo
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
response = Response.redirect("/")
|
response = Response.redirect("/")
|
||||||
response.set_cookie(
|
datasette.set_actor_cookie(response, {"id": "root"})
|
||||||
"ds_actor", datasette.sign({"a": {"id": "root"}}, "actor")
|
|
||||||
)
|
|
||||||
return response
|
return response
|
||||||
return Response.html(
|
return Response.html(
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue