1"""Tests for login redirects"""
2from functools import partial
3from urllib.parse import urlencode
4
5import pytest
6from tornado.httpclient import HTTPClientError
7from tornado.httputil import parse_cookie
8from tornado.httputil import url_concat
9
10from jupyter_server.utils import url_path_join
11
12
13# override default config to ensure a non-empty base url is used
14@pytest.fixture
15def jp_base_url():
16    return "/a%40b/"
17
18
19@pytest.fixture
20def jp_server_config(jp_base_url):
21    return {
22        "ServerApp": {
23            "base_url": jp_base_url,
24        },
25    }
26
27
28async def _login(jp_serverapp, http_server_client, jp_base_url, next):
29    # first: request login page with no creds
30    login_url = url_path_join(jp_base_url, "login")
31    first = await http_server_client.fetch(login_url)
32    cookie_header = first.headers["Set-Cookie"]
33    cookies = parse_cookie(cookie_header)
34
35    # second, submit login form with credentials
36    try:
37        resp = await http_server_client.fetch(
38            url_concat(login_url, {"next": next}),
39            method="POST",
40            body=urlencode(
41                {
42                    "password": jp_serverapp.token,
43                    "_xsrf": cookies.get("_xsrf", ""),
44                }
45            ),
46            headers={"Cookie": cookie_header},
47            follow_redirects=False,
48        )
49    except HTTPClientError as e:
50        if e.code != 302:
51            raise
52        return e.response.headers["Location"]
53    else:
54        assert resp.code == 302, "Should have returned a redirect!"
55
56
57@pytest.fixture
58def login(jp_serverapp, http_server_client, jp_base_url):
59    """Fixture to return a function to login to a Jupyter server
60
61    by submitting the login page form
62    """
63    yield partial(_login, jp_serverapp, http_server_client, jp_base_url)
64
65
66@pytest.mark.parametrize(
67    "bad_next",
68    (
69        r"\\tree",
70        "//some-host",
71        "//host{base_url}tree",
72        "https://google.com",
73        "/absolute/not/base_url",
74    ),
75)
76async def test_next_bad(login, jp_base_url, bad_next):
77    bad_next = bad_next.format(base_url=jp_base_url)
78    url = await login(bad_next)
79    assert url == jp_base_url
80
81
82@pytest.mark.parametrize(
83    "next_path",
84    (
85        "tree/",
86        "//{base_url}tree",
87        "notebooks/notebook.ipynb",
88        "tree//something",
89    ),
90)
91async def test_next_ok(login, jp_base_url, next_path):
92    next_path = next_path.format(base_url=jp_base_url)
93    expected = jp_base_url + next_path
94    actual = await login(next=expected)
95    assert actual == expected
96