File: test_tf_plugin.py

package info (click to toggle)
flask-security 5.6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,420 kB
  • sloc: python: 23,164; javascript: 204; makefile: 138
file content (124 lines) | stat: -rw-r--r-- 4,544 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""
test_tf_plugin
~~~~~~~~~~~~~~~~~

tf_plugin tests

:copyright: (c) 2022-2022 by J. Christopher Wagner (jwag).
:license: MIT, see LICENSE for more details.
"""

import json
import pytest

from tests.test_utils import (
    get_form_action,
    get_session,
    get_existing_session,
    logout,
    setup_tf_sms,
)

from tests.test_two_factor import tf_in_session
from tests.test_webauthn import HackWebauthnUtil, wan_signin, reg_2_keys

pytest.importorskip("webauthn")


@pytest.mark.webauthn(webauthn_util_cls=HackWebauthnUtil)
@pytest.mark.two_factor()
def test_tf_select(app, client, get_message):
    # Test basic select mechanism when more than one 2FA has been setup
    wankeys = reg_2_keys(client)  # add a webauthn 2FA key (authenticates)
    sms_sender = setup_tf_sms(client)
    logout(client)

    # since we have 2 2FA methods configured - we should get the tf-select form
    # also - test that we correctly propagate 'next' all the way through
    response = client.post(
        "/login?next=/profile",
        data=dict(email="matt@lp.com", password="password"),
        follow_redirects=True,
    )
    assert b"Select Two-Factor Method" in response.data
    tf_select_url = get_form_action(response)
    response = client.post(
        tf_select_url, data=dict(which="webauthn"), follow_redirects=True
    )
    assert b"Use Your WebAuthn Security Key as a Second Factor" in response.data
    wan_signin_url = get_form_action(response)
    assert "/wan-signin?next=/profile" == wan_signin_url

    response = wan_signin(
        client, "matt@lp.com", wankeys["secondary"]["signin"], wan_signin_url
    )
    assert not tf_in_session(get_existing_session(client))
    assert b"Profile Page" in response.data

    # now do other 2FA
    logout(client)
    response = client.post(
        "/login",
        data=dict(email="matt@lp.com", password="password"),
        follow_redirects=True,
    )
    assert b"Select Two-Factor Method" in response.data
    response = client.post("/tf-select", data=dict(which="sms"), follow_redirects=True)
    assert b"Please enter your authentication code generated via: SMS" in response.data
    code = sms_sender.messages[0].split()[-1]
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your code has been confirmed" in response.data

    assert not tf_in_session(get_session(response))

    # verify actually logged in
    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200
    assert not tf_in_session(get_existing_session(client))


@pytest.mark.webauthn(webauthn_util_cls=HackWebauthnUtil)
@pytest.mark.two_factor()
def test_tf_select_json(app, client, get_message):
    # Test basic select mechanism when more than one 2FA has been setup
    headers = {"Accept": "application/json", "Content-Type": "application/json"}
    wankeys = reg_2_keys(client)  # add a webauthn 2FA key (authenticates)
    setup_tf_sms(client)
    logout(client)

    # since we have 2 2FA methods configured - we should get the tf-select form
    response = client.post(
        "/login", json=dict(email="matt@lp.com", password="password")
    )
    assert response.json["response"]["tf_required"]
    choices = response.json["response"]["tf_setup_methods"]
    assert all(k in choices for k in ["sms", "webauthn"])

    # should get same answer for GET on /tf-select
    response = client.get("/tf-select", headers=headers)
    choices = response.json["response"]["tf_setup_methods"]
    assert all(k in choices for k in ["sms", "webauthn"])

    # use webauthn as the second factor
    response = client.post("/tf-select", json=dict(which="webauthn"))
    signin_url = response.json["response"]["tf_signin_url"]
    response = client.post(signin_url, headers=headers)
    response_url = f'wan-signin/{response.json["response"]["wan_state"]}'
    response = client.post(
        response_url,
        json=dict(credential=json.dumps(wankeys["secondary"]["signin"])),
    )
    assert response.status_code == 200
    assert not tf_in_session(get_existing_session(client))

    response = client.get("/profile", follow_redirects=False)
    assert response.status_code == 200


@pytest.mark.two_factor()
@pytest.mark.settings(url_prefix="/api")
def test_tf_select_auth(app, client, get_message):
    # /tf-select is an unauthenticated endpoint - make sure only allowable in correct
    # state.
    response = client.get("/api/tf-select", follow_redirects=False)
    assert "/api/login" in response.location