/-/logout page for logging out of ds_actor cookie

Refs #840
This commit is contained in:
Simon Willison 2020-06-28 21:17:30 -07:00
commit 22d932fafc
5 changed files with 83 additions and 0 deletions

View file

@ -33,6 +33,7 @@ from .views.special import (
JsonDataView,
PatternPortfolioView,
AuthTokenView,
LogoutView,
PermissionsDebugView,
MessagesDebugView,
)
@ -853,6 +854,9 @@ class Datasette:
add_route(
AuthTokenView.as_view(self), r"/-/auth-token$",
)
add_route(
LogoutView.as_view(self), r"/-/logout$",
)
add_route(
PermissionsDebugView.as_view(self), r"/-/permissions$",
)

View file

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block title %}Log out{% endblock %}
{% block nav %}
<p class="crumbs">
<a href="{{ base_url }}">home</a>
</p>
{{ super() }}
{% endblock %}
{% block content %}
<h1>Log out</h1>
<p>You are logged in as <strong>{{ actor.id or actor }}</strong></p>
<form action="/-/logout" method="post">
<div>
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
<input type="submit" value="Log out">
</div>
</form>
{% endblock %}

View file

@ -72,6 +72,23 @@ class AuthTokenView(BaseView):
return Response("Invalid token", status=403)
class LogoutView(BaseView):
name = "logout"
def __init__(self, datasette):
self.ds = datasette
async def get(self, request):
if not request.actor:
return Response.redirect("/")
return await self.render(["logout.html"], request, {"actor": request.actor},)
async def post(self, request):
response = Response.redirect("/")
response.set_cookie("ds_actor", "", expires=0, max_age=0)
return response
class PermissionsDebugView(BaseView):
name = "permissions_debug"

View file

@ -404,6 +404,14 @@ The resulting cookie will encode data that looks something like this:
"e": "1jjSji"
}
.. _LogoutView:
The /-/logout page
------------------
The page at ``/-/logout`` provides the ability to log out of a ``ds_actor`` cookie authentication session.
.. _permissions:
Built-in permissions

View file

@ -47,3 +47,32 @@ def test_actor_cookie_that_expires(app_client, offset, expected):
)
response = app_client.get("/", cookies={"ds_actor": cookie})
assert expected == app_client.ds._last_request.scope["actor"]
def test_logout(app_client):
response = app_client.get(
"/-/logout", cookies={"ds_actor": app_client.actor_cookie({"id": "test"})}
)
assert 200 == response.status
assert "<p>You are logged in as <strong>test</strong></p>" in response.text
# Actors without an id get full serialization
response2 = app_client.get(
"/-/logout", cookies={"ds_actor": app_client.actor_cookie({"name2": "bob"})}
)
assert 200 == response2.status
assert (
"<p>You are logged in as <strong>{&#39;name2&#39;: &#39;bob&#39;}</strong></p>"
in response2.text
)
# If logged out you get a redirect to /
response3 = app_client.get("/-/logout", allow_redirects=False)
assert 302 == response3.status
# A POST to that page should log the user out
response4 = app_client.post(
"/-/logout",
csrftoken_from=True,
cookies={"ds_actor": app_client.actor_cookie({"id": "test"})},
allow_redirects=False,
)
assert {"ds_actor": ""} == response4.cookies
assert 302 == response4.status