From 2115d7e3457b48b3cf9c81551b9fed2d0e9cd111 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 29 Jun 2020 11:40:40 -0700 Subject: [PATCH] Logout link in nav, refs #875 --- datasette/app.py | 3 +++ datasette/static/app.css | 18 ++++++++++++++++++ datasette/templates/base.html | 12 +++++++++++- datasette/templates/logout.html | 2 +- datasette/utils/__init__.py | 7 +++++++ tests/test_auth.py | 14 ++++++++++++++ tests/test_utils.py | 19 +++++++++++++++++++ 7 files changed, 73 insertions(+), 2 deletions(-) diff --git a/datasette/app.py b/datasette/app.py index d4c959b7..43249eaa 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -44,6 +44,7 @@ from .database import Database, QueryInterrupted from .utils import ( async_call_with_supported_arguments, call_with_supported_arguments, + display_actor, escape_css_string, escape_sqlite, format_bytes, @@ -736,6 +737,8 @@ class Datasette: template_context = { **context, **{ + "actor": request.actor if request else None, + "display_actor": display_actor, "app_css_hash": self.app_css_hash(), "zip": zip, "body_scripts": body_scripts, diff --git a/datasette/static/app.css b/datasette/static/app.css index 774a2235..ed98b13e 100644 --- a/datasette/static/app.css +++ b/datasette/static/app.css @@ -100,6 +100,14 @@ table a:visited { .hd .crumbs { float: left; } +.hd .logout { + float: right; + text-align: right; + padding-left: 1em; +} +.hd .logout form { + display: inline; +} .ft { margin: 1em 0; padding: 0.5em 1em 0 1em; @@ -367,3 +375,13 @@ p.zero-results { border: 1px solid red; background-color: pink; } + +button.button-as-link { + background: none; + border: none; + padding: 0; + color: blue; + text-decoration: none; + cursor: pointer; + font-size: 1em; +} diff --git a/datasette/templates/base.html b/datasette/templates/base.html index 9b871d03..e739d804 100644 --- a/datasette/templates/base.html +++ b/datasette/templates/base.html @@ -14,7 +14,17 @@ - +
{% block messages %} diff --git a/datasette/templates/logout.html b/datasette/templates/logout.html index 08141962..3c8eb17a 100644 --- a/datasette/templates/logout.html +++ b/datasette/templates/logout.html @@ -13,7 +13,7 @@

Log out

-

You are logged in as {{ actor.id or actor }}

+

You are logged in as {{ display_actor(actor) }}

diff --git a/datasette/utils/__init__.py b/datasette/utils/__init__.py index 30121cf2..6e3fd0db 100644 --- a/datasette/utils/__init__.py +++ b/datasette/utils/__init__.py @@ -924,3 +924,10 @@ def resolve_env_secrets(config, environ): return [resolve_env_secrets(value, environ) for value in config] else: return config + + +def display_actor(actor): + for key in ("display", "name", "username", "login", "id"): + if actor.get(key): + return actor[key] + return str(actor) diff --git a/tests/test_auth.py b/tests/test_auth.py index 145a9a89..1d8148f9 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -78,3 +78,17 @@ def test_logout(app_client): # Should also have set a message messages = app_client.ds.unsign(response4.cookies["ds_messages"], "messages") assert [["You are now logged out", 2]] == messages + + +@pytest.mark.parametrize("path", ["/", "/fixtures", "/fixtures/facetable"]) +def test_logout_button_in_navigation(app_client, path): + response = app_client.get( + path, cookies={"ds_actor": app_client.actor_cookie({"id": "test"})} + ) + anon_response = app_client.get(path) + for fragment in ( + "test ·", + '', + ): + assert fragment in response.text + assert fragment not in anon_response.text diff --git a/tests/test_utils.py b/tests/test_utils.py index 80c6f223..fb2d71f9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -517,3 +517,22 @@ def test_actor_matches_allow(actor, allow, expected): ) def test_resolve_env_secrets(config, expected): assert expected == utils.resolve_env_secrets(config, {"FOO": "x"}) + + +@pytest.mark.parametrize( + "actor,expected", + [ + ({"id": "blah"}, "blah"), + ({"id": "blah", "login": "l"}, "l"), + ({"id": "blah", "login": "l"}, "l"), + ({"id": "blah", "login": "l", "username": "u"}, "u"), + ({"login": "l", "name": "n"}, "n"), + ( + {"id": "blah", "login": "l", "username": "u", "name": "n", "display": "d"}, + "d", + ), + ({"weird": "shape"}, "{'weird': 'shape'}"), + ], +) +def test_display_actor(actor, expected): + assert expected == utils.display_actor(actor)