from bs4 import BeautifulSoup as Soup from .fixtures import app_client from .utils import cookie_was_deleted, last_event from click.testing import CliRunner from datasette.utils import baseconv from datasette.cli import cli import pytest import time @pytest.mark.asyncio async def test_auth_token(ds_client): """The /-/auth-token endpoint sets the correct cookie""" assert ds_client.ds._root_token is not None path = f"/-/auth-token?token={ds_client.ds._root_token}" response = await ds_client.get(path) assert response.status_code == 302 assert "/" == response.headers["Location"] assert {"a": {"id": "root"}} == ds_client.ds.unsign( response.cookies["ds_actor"], "actor" ) # Should have recorded a login event event = last_event(ds_client.ds) assert event.name == "login" assert event.actor == {"id": "root"} # Check that a second with same token fails assert ds_client.ds._root_token is None assert (await ds_client.get(path)).status_code == 403 # But attempting with same token while logged in as root should redirect to / response = await ds_client.get( path, cookies={"ds_actor": ds_client.actor_cookie({"id": "root"})} ) assert response.status_code == 302 assert response.headers["Location"] == "/" @pytest.mark.asyncio async def test_actor_cookie(ds_client): """A valid actor cookie sets request.scope['actor']""" cookie = ds_client.actor_cookie({"id": "test"}) await ds_client.get("/", cookies={"ds_actor": cookie}) assert ds_client.ds._last_request.scope["actor"] == {"id": "test"} @pytest.mark.asyncio async def test_actor_cookie_invalid(ds_client): cookie = ds_client.actor_cookie({"id": "test"}) # Break the signature await ds_client.get("/", cookies={"ds_actor": cookie[:-1] + "."}) assert ds_client.ds._last_request.scope["actor"] is None # Break the cookie format cookie = ds_client.ds.sign({"b": {"id": "test"}}, "actor") await ds_client.get("/", cookies={"ds_actor": cookie}) assert ds_client.ds._last_request.scope["actor"] is None @pytest.mark.asyncio @pytest.mark.parametrize( "offset,expected", [ ((24 * 60 * 60), {"id": "test"}), (-(24 * 60 * 60), None), ], ) async def test_actor_cookie_that_expires(ds_client, offset, expected): expires_at = int(time.time()) + offset cookie = ds_client.ds.sign( {"a": {"id": "test"}, "e": baseconv.base62.encode(expires_at)}, "actor" ) await ds_client.get("/", cookies={"ds_actor": cookie}) assert ds_client.ds._last_request.scope["actor"] == expected def test_logout(app_client): # Keeping app_client for the moment because of csrftoken_from response = app_client.get( "/-/logout", cookies={"ds_actor": app_client.actor_cookie({"id": "test"})} ) assert 200 == response.status assert "
You are logged in as test
" 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 ( "You are logged in as {'name2': 'bob'}
" in response2.text ) # If logged out you get a redirect to / response3 = app_client.get("/-/logout") 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"})}, ) # Should have recorded a logout event event = last_event(app_client.ds) assert event.name == "logout" assert event.actor == {"id": "test"} # The ds_actor cookie should have been unset assert cookie_was_deleted(response4, "ds_actor") # 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.asyncio @pytest.mark.parametrize("path", ["/", "/fixtures", "/fixtures/facetable"]) async def test_logout_button_in_navigation(ds_client, path): response = await ds_client.get( path, cookies={"ds_actor": ds_client.actor_cookie({"id": "test"})} ) anon_response = await ds_client.get(path) for fragment in ( "test", '