summaryrefslogtreecommitdiff
path: root/tests/test_auth_account.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_auth_account.py')
-rw-r--r--tests/test_auth_account.py348
1 files changed, 348 insertions, 0 deletions
diff --git a/tests/test_auth_account.py b/tests/test_auth_account.py
new file mode 100644
index 0000000..68a407b
--- /dev/null
+++ b/tests/test_auth_account.py
@@ -0,0 +1,348 @@
+import os
+import pytest
+from fxa.errors import ClientError
+
+from api import *
+
+def test_create_no_args(client):
+ with pytest.raises(ClientError) as e:
+ client.post("/account/create")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 106,
+ 'error': 'Bad Request',
+ 'message': 'invalid json in request body'
+ }
+
+@pytest.mark.parametrize("args", [
+ {"email": "", "authPW": "", "extra": ""},
+ {"email": "", "authPW": "00"},
+ {"email": "a" * 257, "authPW": "0" * 64},
+ {"email": "a@test", "authPW": "00"},
+ {"email": "a@test", "authPW": "00" * 32, "style": "foo"},
+])
+def test_create_invalid_args(client, args):
+ with pytest.raises(ClientError) as e:
+ client.post("/account/create", args)
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 107,
+ 'error': 'Bad Request',
+ 'message': 'invalid parameter in request body'
+ }
+
+def test_create_nomail(client):
+ with pytest.raises(ClientError) as e:
+ client.create_account("test.account@test-auth", "")
+ assert e.value.details == {
+ 'code': 422,
+ 'errno': 151,
+ 'error': 'Unprocessable Entity',
+ 'message': 'failed to send email'
+ }
+
+def test_create_exists(account):
+ with pytest.raises(ClientError) as e:
+ account.create_account(account.email, "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 101,
+ 'error': 'Bad Request',
+ 'message': 'account already exists'
+ }
+
+@pytest.mark.parametrize("keys", [None, False, True])
+@pytest.mark.parametrize("verify", [False, True])
+def test_create_destroy(client, keys, verify, mail_server):
+ s = client.create_account("test.create.destroy@test-auth", "", keys=keys)
+ try:
+ if verify:
+ (to, body) = mail_server.wait()
+ assert to == [s.email]
+ data = json.loads(base64.urlsafe_b64decode(body.split("#/verify/", maxsplit=1)[1]).decode('utf8'))
+ s.post_a('/recovery_email/verify_code', { 'uid': data['uid'], 'code': data['code'] })
+ if keys:
+ k = s.fetch_keys(s.props['keyFetchToken'], "")
+ with pytest.raises(ClientError) as e:
+ s.fetch_keys(s.props['keyFetchToken'], "")
+ assert e.value.details == {
+ 'code': 401,
+ 'errno': 109,
+ 'error': 'Unauthorized',
+ 'message': 'invalid request signature'
+ }
+ finally:
+ client.destroy_account("test.create.destroy@test-auth", "")
+
+def test_login_no_args(client):
+ with pytest.raises(ClientError) as e:
+ client.post("/account/login")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 106,
+ 'error': 'Bad Request',
+ 'message': 'invalid json in request body'
+ }
+
+@pytest.mark.parametrize("args", [
+ {"email": "", "authPW": "", "extra": ""},
+ {"email": "", "authPW": "00"},
+ {"email": "a" * 257, "authPW": "0" * 64},
+ {"email": "a@test", "authPW": "00"},
+])
+def test_login_invalid_args(client, args):
+ with pytest.raises(ClientError) as e:
+ client.post("/account/login", args)
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 107,
+ 'error': 'Bad Request',
+ 'message': 'invalid parameter in request body'
+ }
+
+def test_login_noaccount(client):
+ with pytest.raises(ClientError) as e:
+ client.login("test@test", "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 102,
+ 'error': 'Bad Request',
+ 'message': 'unknown account'
+ }
+
+def test_login_unverified(client, unverified_account):
+ with pytest.raises(ClientError) as e:
+ client.login(unverified_account.email, "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 104,
+ 'error': 'Bad Request',
+ 'message': 'unverified account'
+ }
+
+def test_login_badcase(account):
+ with pytest.raises(ClientError) as e:
+ account.login(account.email.upper(), "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 120,
+ 'error': 'Bad Request',
+ 'message': 'incorrect email case'
+ }
+
+def test_login_badpasswd(account):
+ with pytest.raises(ClientError) as e:
+ account.login(account.email, "ca0cb780-f114-405a-a5c2-1a4deb933a51")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 103,
+ 'error': 'Bad Request',
+ 'message': 'incorrect password'
+ }
+
+@pytest.mark.parametrize("keys", [None, False, True])
+def test_login(client, account, keys):
+ s = client.login(account.email, "", keys=keys)
+ try:
+ if keys:
+ s.fetch_keys(s.props['keyFetchToken'], "")
+ with pytest.raises(ClientError) as e:
+ s.fetch_keys(s.props['keyFetchToken'], "")
+ assert e.value.details == {
+ 'code': 401,
+ 'errno': 109,
+ 'error': 'Unauthorized',
+ 'message': 'invalid request signature'
+ }
+ finally:
+ s.destroy_session()
+
+@pytest.mark.parametrize("args", [
+ {"email": "", "authPW": "", "extra": ""},
+ {"email": "", "authPW": "00"},
+ {"email": "a" * 257, "authPW": "0" * 64},
+ {"email": "a@test", "authPW": "00"},
+])
+def test_destroy_invalid_args(client, args):
+ with pytest.raises(ClientError) as e:
+ client.post("/account/destroy", args)
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 107,
+ 'error': 'Bad Request',
+ 'message': 'invalid parameter in request body'
+ }
+
+def test_destroy_noaccount(client):
+ with pytest.raises(ClientError) as e:
+ client.destroy_account("test@test", "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 102,
+ 'error': 'Bad Request',
+ 'message': 'unknown account'
+ }
+
+def test_destroy_badcase(account):
+ with pytest.raises(ClientError) as e:
+ account.destroy_account(account.email.upper(), "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 120,
+ 'error': 'Bad Request',
+ 'message': 'incorrect email case'
+ }
+
+def test_destroy_badpasswd(account):
+ with pytest.raises(ClientError) as e:
+ account.destroy_account(account.email, "ca0cb780-f114-405a-a5c2-1a4deb933a51")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 103,
+ 'error': 'Bad Request',
+ 'message': 'incorrect password'
+ }
+
+@pytest.mark.parametrize("auth", [
+ {},
+ {"authorization": "bearer invalid"},
+ {"authorization": "hawk id=invalid"},
+])
+def test_keys_invalid(client, auth):
+ with pytest.raises(ClientError) as e:
+ client.get("/account/keys", headers=auth)
+ assert e.value.details == {
+ 'code': 401,
+ 'errno': 109,
+ 'error': 'Unauthorized',
+ 'message': 'invalid request signature'
+ }
+
+# create and login do the remaining keyfetch tests.
+# we don't check whether the bundle is actually *valid* because we'd have to look
+# into the db to do a full test, so we'll trust it is correct if sync works.
+
+@pytest.fixture
+def reset_token(account, forgot_token, mail_server):
+ (to, body) = mail_server.wait()
+ assert account.email in to
+ resp = forgot_token.post_a("/password/forgot/verify_code", { 'code': body.strip() })
+ return AccountReset(account.client, resp['accountResetToken'])
+
+@pytest.mark.parametrize("args", [
+ { 'authPW': '00', },
+ { 'authPW': '00' * 32, 'extra': 0, },
+])
+def test_reset_invalid(reset_token, args):
+ with pytest.raises(ClientError) as e:
+ reset_token.post_a("/account/reset", args)
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 107,
+ 'error': 'Bad Request',
+ 'message': 'invalid parameter in request body'
+ }
+
+def test_reset(account, reset_token, mail_server, push_server):
+ dev = Device(account, "dev", pcb=push_server.good("d4105515-f4f0-4d26-85c1-f48c5c348edb"))
+ resp = reset_token.post_a("/account/reset", { 'authPW': auth_pw(account.email, "") })
+ assert resp == {}
+ (to, body) = mail_server.wait()
+ assert account.email in to
+ assert 'account has been reset' in body
+ p = push_server.wait()
+ assert p[0] == "/d4105515-f4f0-4d26-85c1-f48c5c348edb"
+ assert dev.decrypt(p[2]) == {'command': 'fxaccounts:password_reset', 'version': 1}
+ p = push_server.wait()
+ assert p[0] == "/d4105515-f4f0-4d26-85c1-f48c5c348edb"
+ assert dev.decrypt(p[2]) == {
+ 'command': 'fxaccounts:device_disconnected',
+ 'data': {'id': dev.id},
+ 'version': 1
+ }
+ assert push_server.done()
+
+def test_reset(account, reset_token, mail_server, push_server):
+ reset_token.post_a("/account/reset", { 'authPW': auth_pw(account.email, "") })
+ with pytest.raises(ClientError) as e:
+ reset_token.post_a("/account/reset", { 'authPW': auth_pw(account.email, "") })
+ assert e.value.details == {
+ 'code': 401,
+ 'errno': 109,
+ 'error': 'Unauthorized',
+ 'message': 'invalid request signature'
+ }
+
+@pytest.mark.invite
+def test_create_noinvite(client):
+ with pytest.raises(ClientError) as e:
+ client.create_account("test.create.destroy@test-auth", "")
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': -1,
+ 'error': 'Bad Request',
+ 'message': 'invite code required'
+ }
+
+@pytest.mark.invite
+def test_create_badinvite(client):
+ with pytest.raises(ClientError) as e:
+ client.create_account("test.create.destroy@test-auth", "", { 'style': '' })
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': -1,
+ 'error': 'Bad Request',
+ 'message': 'invite code required'
+ }
+
+@pytest.mark.invite
+def test_create_invite(client, mail_server):
+ # all in one test because restarting the server from python would be a pain
+ c = client.create_account("test.account@test-auth", "", invite=os.environ['INVITE_CODE'])
+ c2 = None
+ try:
+ invite = Invite(c.props['sessionToken'])
+ # unverified sessions fail
+ with pytest.raises(ClientError) as e:
+ code = invite.post_a('/generate', { 'ttl_hours': 1 })
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': 138,
+ 'error': 'Bad Request',
+ 'message': 'unverified session'
+ }
+ # verified session works
+ (to, body) = mail_server.wait()
+ data = json.loads(base64.urlsafe_b64decode(body.split("#/verify/", maxsplit=1)[1]).decode('utf8'))
+ c.post_a('/recovery_email/verify_code', { 'uid': data['uid'], 'code': data['code'] })
+ code = invite.post_a('/generate', { 'ttl_hours': 1 })
+ assert 'url' in code
+ code = code['url'].split('/')[-1]
+ # code allows registration
+ c2 = client.create_account("test.account2@test-auth", "", invite=code)
+ (to, body) = mail_server.wait()
+ data = json.loads(base64.urlsafe_b64decode(body.split("#/verify/", maxsplit=1)[1]).decode('utf8'))
+ c2.post_a('/recovery_email/verify_code', { 'uid': data['uid'], 'code': data['code'] })
+ # token is consumed
+ with pytest.raises(ClientError) as e:
+ client.create_account("test.account3@test-auth", "", invite=code)
+ assert e.value.details == {
+ 'code': 400,
+ 'errno': -2,
+ 'error': 'Bad Request',
+ 'message': 'invite code not found'
+ }
+ # non-admin accounts can't generate tokens
+ with pytest.raises(ClientError) as e:
+ invite2 = Invite(c2.props['sessionToken'])
+ invite2.post_a('/generate', { 'ttl_hours': 1 })
+ assert e.value.details == {
+ 'code': 401,
+ 'errno': 110,
+ 'error': 'Unauthorized',
+ 'message': 'invalid authentication token'
+ }
+ finally:
+ c.destroy_account(c.email, "")
+ if c2 is not None:
+ c2.destroy_account(c2.email, "")