mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-05 06:01:46 -04:00
Integrate with the API for adding and getting webauthn creds
This links up the `get_webauthn_credentials_for_user` and
`create_webauthn_credential_for_user` methods of the user api client to
notifications-api.
To send data to the API we need strings to be unicode, so we call
decode('utf-8') on base64 objects.
Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
This commit is contained in:
@@ -19,8 +19,10 @@ def test_should_show_overview_page(
|
||||
|
||||
def test_overview_page_shows_disable_for_platform_admin(
|
||||
client_request,
|
||||
platform_admin_user
|
||||
platform_admin_user,
|
||||
mocker
|
||||
):
|
||||
mocker.patch('app.user_api_client.get_webauthn_credentials_for_user')
|
||||
client_request.login(platform_admin_user)
|
||||
page = client_request.get('main.user_profile')
|
||||
assert page.select_one('h1').text.strip() == 'Your profile'
|
||||
|
||||
@@ -27,6 +27,7 @@ def test_begin_register_returns_encoded_options(
|
||||
values={'ADMIN_BASE_URL': 'http://localhost:6012'}
|
||||
)
|
||||
webauthn_server.init_app(app_)
|
||||
mocker.patch('app.user_api_client.get_webauthn_credentials_for_user', return_value=[])
|
||||
|
||||
response = platform_admin_client.get(
|
||||
url_for('main.webauthn_begin_register')
|
||||
@@ -71,11 +72,18 @@ def test_begin_register_includes_existing_credentials(
|
||||
|
||||
def test_begin_register_stores_state_in_session(
|
||||
platform_admin_client,
|
||||
mocker,
|
||||
):
|
||||
platform_admin_client.get(
|
||||
mocker.patch(
|
||||
'app.user_api_client.get_webauthn_credentials_for_user',
|
||||
return_value=[])
|
||||
|
||||
response = platform_admin_client.get(
|
||||
url_for('main.webauthn_begin_register')
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
with platform_admin_client.session_transaction() as session:
|
||||
assert session['webauthn_registration_state'] is not None
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import base64
|
||||
|
||||
import pytest
|
||||
from fido2 import cbor
|
||||
from fido2.cose import ES256
|
||||
|
||||
@@ -14,7 +15,8 @@ CLIENT_DATA_JSON = b'{"type": "webauthn.create", "clientExtensions": {}, "challe
|
||||
ATTESTATION_OBJECT = base64.b64decode(b'o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2lnWEgwRgIhAI1qbvWibQos/t3zsTU05IXw1Ek3SDApATok09uc4UBwAiEAv0fB/lgb5Ot3zJ691Vje6iQLAtLhJDiA8zDxaGjcE3hjeDVjgVkCUzCCAk8wggE3oAMCAQICBDxoKU0wDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMDExLzAtBgNVBAMMJll1YmljbyBVMkYgRUUgU2VyaWFsIDIzOTI1NzM0ODExMTE3OTAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvd9nk9t3lMNQMXHtLE1FStlzZnUaSLql2fm1ajoggXlrTt8rzXuSehSTEPvEaEdv/FeSqX22L6Aoa8ajIAIOY6M7MDkwIgYJKwYBBAGCxAoCBBUxLjMuNi4xLjQuMS40MTQ4Mi4xLjUwEwYLKwYBBAGC5RwCAQEEBAMCBSAwDQYJKoZIhvcNAQELBQADggEBAKrADVEJfuwVpIazebzEg0D4Z9OXLs5qZ/ukcONgxkRZ8K04QtP/CB5x6olTlxsj+SXArQDCRzEYUgbws6kZKfuRt2a1P+EzUiqDWLjRILSr+3/o7yR7ZP/GpiFKwdm+czb94POoGD+TS1IYdfXj94mAr5cKWx4EKjh210uovu/pLdLjc8xkQciUrXzZpPR9rT2k/q9HkZhHU+NaCJzky+PTyDbq0KKnzqVhWtfkSBCGw3ezZkTS+5lrvOKbIa24lfeTgu7FST5OwTPCFn8HcfWZMXMSD/KNU+iBqJdAwTLPPDRoLLvPTl29weCAIh+HUpmBQd0UltcPOrA/LFvAf61oYXV0aERhdGFYwnSm6pITyZwvdLIkkrMgz0AmKpTBqVCgOX8pJQtghB7wQQAAAAAAAAAAAAAAAAAAAAAAAAAAAECKU1ppjl9gmhHWyDkgHsUvZmhr6oF3/lD3llzLE2SaOSgOGIsIuAQqgp8JQSUu3r/oOaP8RS44dlQjrH+ALfYtpAECAyYhWCAxnqAfESXOYjKUc2WACuXZ3ch0JHxV0VFrrTyjyjIHXCJYIFnx8H87L4bApR4M+hPcV+fHehEOeW+KCyd0H+WGY8s6') # noqa
|
||||
|
||||
|
||||
def test_from_registration_verifies_response(app_, mocker):
|
||||
@pytest.fixture
|
||||
def disable_webauthn_origin_verification(app_, mocker):
|
||||
mocker.patch.dict(
|
||||
app_.config, values={
|
||||
'NOTIFY_ENVIRONMENT': 'development',
|
||||
@@ -25,6 +27,8 @@ def test_from_registration_verifies_response(app_, mocker):
|
||||
# disable origin verification for non-HTTPS test
|
||||
webauthn_server.init_app(app_)
|
||||
|
||||
|
||||
def test_from_registration_verifies_response(disable_webauthn_origin_verification):
|
||||
registration_response = {
|
||||
'clientDataJSON': CLIENT_DATA_JSON,
|
||||
'attestationObject': ATTESTATION_OBJECT,
|
||||
@@ -32,9 +36,23 @@ def test_from_registration_verifies_response(app_, mocker):
|
||||
|
||||
credential = WebAuthnCredential.from_registration(SESSION_STATE, registration_response)
|
||||
assert credential.name == 'Unnamed key'
|
||||
assert credential.registration_response == base64.b64encode(cbor.encode(registration_response))
|
||||
assert credential.registration_response == base64.b64encode(cbor.encode(registration_response)).decode('utf-8')
|
||||
|
||||
credential_data = credential.to_credential_data()
|
||||
assert type(credential_data.credential_id) is bytes
|
||||
assert type(credential_data.aaguid) is bytes
|
||||
assert credential_data.public_key[3] == ES256.ALGORITHM
|
||||
|
||||
|
||||
def test_from_registration_encodes_as_unicode(disable_webauthn_origin_verification):
|
||||
registration_response = {
|
||||
'clientDataJSON': CLIENT_DATA_JSON,
|
||||
'attestationObject': ATTESTATION_OBJECT,
|
||||
}
|
||||
|
||||
credential = WebAuthnCredential.from_registration(SESSION_STATE, registration_response)
|
||||
|
||||
serialized_credential = credential.serialize()
|
||||
|
||||
assert type(serialized_credential['credential_data']) == str
|
||||
assert type(serialized_credential['registration_response']) == str
|
||||
|
||||
@@ -241,12 +241,25 @@ def test_add_user_to_service_calls_correct_endpoint_and_deletes_keys_from_cache(
|
||||
]
|
||||
|
||||
|
||||
def test_get_webauthn_credentials_for_user_returns_stubbed_data():
|
||||
credentials = user_api_client.get_webauthn_credentials_for_user('id')
|
||||
assert len(credentials) == 0
|
||||
def test_get_webauthn_credentials_for_user(mocker, webauthn_credential, fake_uuid):
|
||||
|
||||
mock_get = mocker.patch(
|
||||
'app.notify_client.user_api_client.UserApiClient.get',
|
||||
return_value={'data': [webauthn_credential]}
|
||||
)
|
||||
|
||||
credentials = user_api_client.get_webauthn_credentials_for_user(fake_uuid)
|
||||
|
||||
mock_get.assert_called_once_with(f'/user/{fake_uuid}/webauthn')
|
||||
assert len(credentials) == 1
|
||||
assert credentials[0]['name'] == 'Test credential'
|
||||
|
||||
|
||||
def test_create_webauthn_credential_for_user_stores_stubbed_data(webauthn_credential):
|
||||
def test_create_webauthn_credential_for_user(mocker, webauthn_credential, fake_uuid):
|
||||
credential = WebAuthnCredential(webauthn_credential)
|
||||
user_api_client.create_webauthn_credential_for_user('id', credential)
|
||||
assert len(user_api_client.credentials) == 1
|
||||
|
||||
mock_post = mocker.patch('app.notify_client.user_api_client.UserApiClient.post')
|
||||
expected_url = f'/user/{fake_uuid}/webauthn'
|
||||
|
||||
user_api_client.create_webauthn_credential_for_user(fake_uuid, credential)
|
||||
mock_post.assert_called_once_with(expected_url, data=credential.serialize())
|
||||
|
||||
@@ -4485,7 +4485,7 @@ def mock_get_invited_org_user_by_id(mocker, sample_org_invite):
|
||||
def webauthn_credential():
|
||||
return {
|
||||
'name': 'Test credential',
|
||||
'credential_data': b'WJ0AAAAAAAAAAAAAAAAAAAAAAECKU1ppjl9gmhHWyDkgHsUvZmhr6oF3/lD3llzLE2SaOSgOGIsIuAQqgp8JQSUu3r/oOaP8RS44dlQjrH+ALfYtpAECAyYhWCAxnqAfESXOYjKUc2WACuXZ3ch0JHxV0VFrrTyjyjIHXCJYIFnx8H87L4bApR4M+hPcV+fHehEOeW+KCyd0H+WGY8s6', # noqa
|
||||
'credential_data': 'WJ0AAAAAAAAAAAAAAAAAAAAAAECKU1ppjl9gmhHWyDkgHsUvZmhr6oF3/lD3llzLE2SaOSgOGIsIuAQqgp8JQSUu3r/oOaP8RS44dlQjrH+ALfYtpAECAyYhWCAxnqAfESXOYjKUc2WACuXZ3ch0JHxV0VFrrTyjyjIHXCJYIFnx8H87L4bApR4M+hPcV+fHehEOeW+KCyd0H+WGY8s6', # noqa
|
||||
'registration_response': 'anything',
|
||||
'created_at': '2017-10-18T16:57:14.154185Z',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user