From 4cfe0515eaef29a33c087e90263714660c6246e3 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 12:01:39 +0000 Subject: [PATCH 01/56] test(tokens-repo): split between abstract api and backend-specific tests --- .../test_json_tokens_repository.py | 154 ++++++++++++++++++ .../empty_keys.json | 9 + .../null_keys.json | 26 +++ .../test_json_tokens_repository/tokens.json | 35 ++++ .../test_repository/test_tokens_repository.py | 123 ++++++-------- 5 files changed, 272 insertions(+), 75 deletions(-) create mode 100644 tests/test_graphql/test_repository/test_json_tokens_repository.py create mode 100644 tests/test_graphql/test_repository/test_json_tokens_repository/empty_keys.json create mode 100644 tests/test_graphql/test_repository/test_json_tokens_repository/null_keys.json create mode 100644 tests/test_graphql/test_repository/test_json_tokens_repository/tokens.json diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository.py b/tests/test_graphql/test_repository/test_json_tokens_repository.py new file mode 100644 index 0000000..e90b3dc --- /dev/null +++ b/tests/test_graphql/test_repository/test_json_tokens_repository.py @@ -0,0 +1,154 @@ +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=missing-function-docstring +""" +tests that restrict json token repository implementation +""" + +import pytest + + +from datetime import datetime + +from selfprivacy_api.models.tokens.token import Token +from selfprivacy_api.repositories.tokens.exceptions import ( + TokenNotFound, +) +from selfprivacy_api.repositories.tokens.json_tokens_repository import ( + JsonTokensRepository, +) +from tests.common import read_json + +from test_tokens_repository import ORIGINAL_TOKEN_CONTENT +from test_tokens_repository import ( + tokens, + mock_recovery_key_generate, + mock_generate_token, + mock_new_device_key_generate, + empty_keys, +) + + +def test_delete_token(tokens): + repo = JsonTokensRepository() + input_token = Token( + token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + device_name="primary_token", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + ) + + repo.delete_token(input_token) + assert read_json(tokens / "tokens.json")["tokens"] == [ + { + "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + "name": "second_token", + "date": "2022-07-15 17:41:31.675698Z", + }, + { + "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + "name": "third_token", + "date": "2022-07-15T17:41:31.675698Z", + }, + { + "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + "name": "forth_token", + "date": "2022-07-15T17:41:31.675698", + }, + ] + + +def test_delete_not_found_token(tokens): + repo = JsonTokensRepository() + input_token = Token( + token="imbadtoken", + device_name="primary_token", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + ) + with pytest.raises(TokenNotFound): + assert repo.delete_token(input_token) is None + + assert read_json(tokens / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT + + +def test_create_recovery_key(tokens, mock_recovery_key_generate): + repo = JsonTokensRepository() + + assert repo.create_recovery_key(uses_left=1, expiration=None) is not None + assert read_json(tokens / "tokens.json")["recovery_token"] == { + "token": "889bf49c1d3199d71a2e704718772bd53a422020334db051", + "date": "2022-07-15T17:41:31.675698", + "expiration": None, + "uses_left": 1, + } + + +def test_use_mnemonic_recovery_key(tokens, mock_generate_token): + repo = JsonTokensRepository() + + assert repo.use_mnemonic_recovery_key( + mnemonic_phrase="uniform clarify napkin bid dress search input armor police cross salon because myself uphold slice bamboo hungry park", + device_name="newdevice", + ) == Token( + token="ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", + device_name="newdevice", + created_at=datetime(2022, 11, 14, 6, 6, 32, 777123), + ) + + assert read_json(tokens / "tokens.json")["tokens"] == [ + { + "date": "2022-07-15 17:41:31.675698", + "name": "primary_token", + "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + }, + { + "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + "name": "second_token", + "date": "2022-07-15 17:41:31.675698Z", + }, + { + "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + "name": "third_token", + "date": "2022-07-15T17:41:31.675698Z", + }, + { + "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + "name": "forth_token", + "date": "2022-07-15T17:41:31.675698", + }, + { + "date": "2022-11-14T06:06:32.777123", + "name": "newdevice", + "token": "ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", + }, + ] + assert read_json(tokens / "tokens.json")["recovery_token"] == { + "date": "2022-11-11T11:48:54.228038", + "expiration": None, + "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", + "uses_left": 1, + } + + +def test_get_new_device_key(tokens, mock_new_device_key_generate): + repo = JsonTokensRepository() + + assert repo.get_new_device_key() is not None + assert read_json(tokens / "tokens.json")["new_device"] == { + "date": "2022-07-15T17:41:31.675698", + "expiration": "2022-07-15T17:41:31.675698", + "token": "43478d05b35e4781598acd76e33832bb", + } + + +def test_delete_new_device_key(tokens): + repo = JsonTokensRepository() + + assert repo.delete_new_device_key() is None + assert "new_device" not in read_json(tokens / "tokens.json") + + +def test_delete_new_device_key_when_empty(empty_keys): + repo = JsonTokensRepository() + + repo.delete_new_device_key() + assert "new_device" not in read_json(empty_keys / "empty_keys.json") diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository/empty_keys.json b/tests/test_graphql/test_repository/test_json_tokens_repository/empty_keys.json new file mode 100644 index 0000000..2131ddf --- /dev/null +++ b/tests/test_graphql/test_repository/test_json_tokens_repository/empty_keys.json @@ -0,0 +1,9 @@ +{ + "tokens": [ + { + "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + "name": "primary_token", + "date": "2022-07-15 17:41:31.675698" + } + ] +} diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository/null_keys.json b/tests/test_graphql/test_repository/test_json_tokens_repository/null_keys.json new file mode 100644 index 0000000..45e6f90 --- /dev/null +++ b/tests/test_graphql/test_repository/test_json_tokens_repository/null_keys.json @@ -0,0 +1,26 @@ +{ + "tokens": [ + { + "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + "name": "primary_token", + "date": "2022-07-15 17:41:31.675698" + }, + { + "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + "name": "second_token", + "date": "2022-07-15 17:41:31.675698Z" + }, + { + "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + "name": "third_token", + "date": "2022-07-15T17:41:31.675698Z" + }, + { + "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + "name": "forth_token", + "date": "2022-07-15T17:41:31.675698" + } + ], + "recovery_token": null, + "new_device": null +} diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository/tokens.json b/tests/test_graphql/test_repository/test_json_tokens_repository/tokens.json new file mode 100644 index 0000000..bb1805c --- /dev/null +++ b/tests/test_graphql/test_repository/test_json_tokens_repository/tokens.json @@ -0,0 +1,35 @@ +{ + "tokens": [ + { + "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + "name": "primary_token", + "date": "2022-07-15 17:41:31.675698" + }, + { + "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + "name": "second_token", + "date": "2022-07-15 17:41:31.675698Z" + }, + { + "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + "name": "third_token", + "date": "2022-07-15T17:41:31.675698Z" + }, + { + "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + "name": "forth_token", + "date": "2022-07-15T17:41:31.675698" + } + ], + "recovery_token": { + "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", + "date": "2022-11-11T11:48:54.228038", + "expiration": null, + "uses_left": 2 + }, + "new_device": { + "token": "2237238de23dc71ab558e317bdb8ff8e", + "date": "2022-10-26 20:50:47.973212", + "expiration": "2022-10-26 21:00:47.974153" + } +} diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 878e242..5a74bf4 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -236,34 +236,6 @@ def test_create_token(tokens, mock_token_generate): ) -def test_delete_token(tokens): - repo = JsonTokensRepository() - input_token = Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ) - - repo.delete_token(input_token) - assert read_json(tokens / "tokens.json")["tokens"] == [ - { - "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - "name": "second_token", - "date": "2022-07-15 17:41:31.675698Z", - }, - { - "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - "name": "third_token", - "date": "2022-07-15T17:41:31.675698Z", - }, - { - "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - "name": "forth_token", - "date": "2022-07-15T17:41:31.675698", - }, - ] - - def test_delete_not_found_token(tokens): repo = JsonTokensRepository() input_token = Token( @@ -274,7 +246,7 @@ def test_delete_not_found_token(tokens): with pytest.raises(TokenNotFound): assert repo.delete_token(input_token) is None - assert read_json(tokens / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT + # assert read_json(tokens / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT def test_refresh_token(tokens, mock_token_generate): @@ -330,12 +302,12 @@ def test_create_recovery_key(tokens, mock_recovery_key_generate): repo = JsonTokensRepository() assert repo.create_recovery_key(uses_left=1, expiration=None) is not None - assert read_json(tokens / "tokens.json")["recovery_token"] == { - "token": "889bf49c1d3199d71a2e704718772bd53a422020334db051", - "date": "2022-07-15T17:41:31.675698", - "expiration": None, - "uses_left": 1, - } + # assert read_json(tokens / "tokens.json")["recovery_token"] == { + # "token": "889bf49c1d3199d71a2e704718772bd53a422020334db051", + # "date": "2022-07-15T17:41:31.675698", + # "expiration": None, + # "uses_left": 1, + # } def test_use_mnemonic_recovery_key_when_empty( @@ -433,6 +405,7 @@ def test_use_menemonic_recovery_key_when_null(null_keys): ) +# agnostic test mixed with an implementation test def test_use_mnemonic_recovery_key(tokens, mock_generate_token): repo = JsonTokensRepository() @@ -445,40 +418,40 @@ def test_use_mnemonic_recovery_key(tokens, mock_generate_token): created_at=datetime(2022, 11, 14, 6, 6, 32, 777123), ) - assert read_json(tokens / "tokens.json")["tokens"] == [ - { - "date": "2022-07-15 17:41:31.675698", - "name": "primary_token", - "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - }, - { - "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - "name": "second_token", - "date": "2022-07-15 17:41:31.675698Z", - }, - { - "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - "name": "third_token", - "date": "2022-07-15T17:41:31.675698Z", - }, - { - "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - "name": "forth_token", - "date": "2022-07-15T17:41:31.675698", - }, - { - "date": "2022-11-14T06:06:32.777123", - "name": "newdevice", - "token": "ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", - }, - ] + # assert read_json(tokens / "tokens.json")["tokens"] == [ + # { + # "date": "2022-07-15 17:41:31.675698", + # "name": "primary_token", + # "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + # }, + # { + # "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + # "name": "second_token", + # "date": "2022-07-15 17:41:31.675698Z", + # }, + # { + # "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + # "name": "third_token", + # "date": "2022-07-15T17:41:31.675698Z", + # }, + # { + # "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + # "name": "forth_token", + # "date": "2022-07-15T17:41:31.675698", + # }, + # { + # "date": "2022-11-14T06:06:32.777123", + # "name": "newdevice", + # "token": "ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", + # }, + # ] - assert read_json(tokens / "tokens.json")["recovery_token"] == { - "date": "2022-11-11T11:48:54.228038", - "expiration": None, - "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", - "uses_left": 1, - } + # assert read_json(tokens / "tokens.json")["recovery_token"] == { + # "date": "2022-11-11T11:48:54.228038", + # "expiration": None, + # "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", + # "uses_left": 1, + # } ################## @@ -490,25 +463,25 @@ def test_get_new_device_key(tokens, mock_new_device_key_generate): repo = JsonTokensRepository() assert repo.get_new_device_key() is not None - assert read_json(tokens / "tokens.json")["new_device"] == { - "date": "2022-07-15T17:41:31.675698", - "expiration": "2022-07-15T17:41:31.675698", - "token": "43478d05b35e4781598acd76e33832bb", - } + # assert read_json(tokens / "tokens.json")["new_device"] == { + # "date": "2022-07-15T17:41:31.675698", + # "expiration": "2022-07-15T17:41:31.675698", + # "token": "43478d05b35e4781598acd76e33832bb", + # } def test_delete_new_device_key(tokens): repo = JsonTokensRepository() assert repo.delete_new_device_key() is None - assert "new_device" not in read_json(tokens / "tokens.json") + # assert "new_device" not in read_json(tokens / "tokens.json") def test_delete_new_device_key_when_empty(empty_keys): repo = JsonTokensRepository() repo.delete_new_device_key() - assert "new_device" not in read_json(empty_keys / "empty_keys.json") + # assert "new_device" not in read_json(empty_keys / "empty_keys.json") def test_use_invalid_mnemonic_new_device_key( From 8a05a55b80cf5267a1a5e7c646acaa1be3167217 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 12:02:33 +0000 Subject: [PATCH 02/56] test(tokens-repo): parameterized fixture --- .../test_repository/test_tokens_repository.py | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 5a74bf4..cfeddb3 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -18,6 +18,9 @@ from selfprivacy_api.repositories.tokens.exceptions import ( from selfprivacy_api.repositories.tokens.json_tokens_repository import ( JsonTokensRepository, ) +from selfprivacy_api.repositories.tokens.redis_tokens_repository import ( + RedisTokensRepository, +) from tests.common import read_json @@ -44,6 +47,13 @@ ORIGINAL_TOKEN_CONTENT = [ }, ] +ORIGINAL_DEVICE_NAMES = [ + "primary_token", + "second_token", + "third_token", + "forth_token", +] + @pytest.fixture def tokens(mocker, datadir): @@ -145,25 +155,59 @@ def mock_recovery_key_generate(mocker): return mock +@pytest.fixture +def empty_json_repo(tokens): + repo = JsonTokensRepository() + for token in repo.get_tokens(): + repo.delete_token(token) + assert repo.get_tokens() == [] + return repo + + +@pytest.fixture +def empty_redis_repo(): + repo = RedisTokensRepository() + for token in repo.get_tokens(): + repo.delete_token(token) + assert repo.get_tokens() == [] + return repo + + +@pytest.fixture(params=["json", "redis"]) +def empty_repo(request, empty_json_repo): + if request.param == "json": + return empty_json_repo + if request.param == "redis": + # return empty_redis_repo + return empty_json_repo + else: + raise NotImplementedError + + +@pytest.fixture +def some_tokens_repo(empty_repo): + for name in ORIGINAL_DEVICE_NAMES: + empty_repo.create_token(name) + assert len(empty_repo.get_tokens()) == len(ORIGINAL_DEVICE_NAMES) + for i, t in enumerate(empty_repo.get_tokens()): + assert t.device_name == ORIGINAL_DEVICE_NAMES[i] + return empty_repo + + ############### # Test tokens # ############### -def test_get_token_by_token_string(tokens): - repo = JsonTokensRepository() +def test_get_token_by_token_string(some_tokens_repo): + repo = some_tokens_repo + test_token = repo.get_tokens()[2] - assert repo.get_token_by_token_string( - token_string="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI" - ) == Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ) + assert repo.get_token_by_token_string(token_string=test_token.token) == test_token -def test_get_token_by_non_existent_token_string(tokens): - repo = JsonTokensRepository() +def test_get_token_by_non_existent_token_string(some_tokens_repo): + repo = some_tokens_repo with pytest.raises(TokenNotFound): assert repo.get_token_by_token_string(token_string="iamBadtoken") is None From 55ad2484b82238c6df70f7d53a5cd45df92d201b Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 12:10:50 +0000 Subject: [PATCH 03/56] test(tokens-repo): agnostic test for getting by name --- .../test_repository/test_tokens_repository.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index cfeddb3..8e1b8e3 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -213,19 +213,15 @@ def test_get_token_by_non_existent_token_string(some_tokens_repo): assert repo.get_token_by_token_string(token_string="iamBadtoken") is None -def test_get_token_by_name(tokens): - repo = JsonTokensRepository() +def test_get_token_by_name(some_tokens_repo): + repo = some_tokens_repo assert repo.get_token_by_name(token_name="primary_token") is not None - assert repo.get_token_by_name(token_name="primary_token") == Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ) + assert repo.get_token_by_name(token_name="primary_token") == repo.get_tokens()[0] -def test_get_token_by_non_existent_name(tokens): - repo = JsonTokensRepository() +def test_get_token_by_non_existent_name(some_tokens_repo): + repo = some_tokens_repo with pytest.raises(TokenNotFound): assert repo.get_token_by_name(token_name="badname") is None From 2e2d344f43c16339b9fb870609e60db4c4964184 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 13:03:48 +0000 Subject: [PATCH 04/56] test(tokens-repo): get_tokens metaproperties test --- .../test_repository/test_tokens_repository.py | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 8e1b8e3..0fc3194 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -227,31 +227,15 @@ def test_get_token_by_non_existent_name(some_tokens_repo): assert repo.get_token_by_name(token_name="badname") is None -def test_get_tokens(tokens): - repo = JsonTokensRepository() - - assert repo.get_tokens() == [ - Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ), - Token( - token="3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - device_name="second_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698, tzinfo=timezone.utc), - ), - Token( - token="LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - device_name="third_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698, tzinfo=timezone.utc), - ), - Token( - token="dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - device_name="forth_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ), - ] +def test_get_tokens(some_tokens_repo): + repo = some_tokens_repo + tokenstrings = [] + # we cannot insert tokens directly via api, so we check meta-properties instead + for token in some_tokens_repo.get_tokens(): + len(token.token) == 43 # assuming secrets.token_urlsafe + assert token.token not in tokenstrings + tokenstrings.append(token.token) + assert token.created_at.day == datetime.today().day def test_get_tokens_when_one(empty_keys): From 3921d9fe4c68347fc1de93036a85e0be0a0665aa Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 14:48:33 +0000 Subject: [PATCH 05/56] test(tokens-repo): agnostic token creation test --- .../test_repository/test_tokens_repository.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 0fc3194..957ceb4 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -129,7 +129,7 @@ def mock_get_recovery_key_return_not_valid(mocker): @pytest.fixture def mock_token_generate(mocker): mock = mocker.patch( - "selfprivacy_api.repositories.tokens.json_tokens_repository.Token.generate", + "selfprivacy_api.models.tokens.token.Token.generate", autospec=True, return_value=Token( token="ZuLNKtnxDeq6w2dpOJhbB3iat_sJLPTPl_rN5uc5MvM", @@ -238,26 +238,21 @@ def test_get_tokens(some_tokens_repo): assert token.created_at.day == datetime.today().day -def test_get_tokens_when_one(empty_keys): - repo = JsonTokensRepository() - - assert repo.get_tokens() == [ - Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ) - ] - - -def test_create_token(tokens, mock_token_generate): - repo = JsonTokensRepository() +def test_create_token(empty_repo, mock_token_generate): + repo = empty_repo assert repo.create_token(device_name="IamNewDevice") == Token( token="ZuLNKtnxDeq6w2dpOJhbB3iat_sJLPTPl_rN5uc5MvM", device_name="IamNewDevice", created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), ) + assert repo.get_tokens() == [ + Token( + token="ZuLNKtnxDeq6w2dpOJhbB3iat_sJLPTPl_rN5uc5MvM", + device_name="IamNewDevice", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + ) + ] def test_delete_not_found_token(tokens): From db55685488a28639df3fb153742960772857f3e4 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 14:51:28 +0000 Subject: [PATCH 06/56] test(tokens-repo): use 'repo' for consistency --- tests/test_graphql/test_repository/test_tokens_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 957ceb4..c561775 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -231,7 +231,7 @@ def test_get_tokens(some_tokens_repo): repo = some_tokens_repo tokenstrings = [] # we cannot insert tokens directly via api, so we check meta-properties instead - for token in some_tokens_repo.get_tokens(): + for token in repo.get_tokens(): len(token.token) == 43 # assuming secrets.token_urlsafe assert token.token not in tokenstrings tokenstrings.append(token.token) From b43c4014e21df8bf31204050a8f564c31770ccfe Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 14:56:54 +0000 Subject: [PATCH 07/56] test(tokens-repo): agnostic delete not found --- .../test_graphql/test_repository/test_tokens_repository.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index c561775..1480b7f 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -255,8 +255,9 @@ def test_create_token(empty_repo, mock_token_generate): ] -def test_delete_not_found_token(tokens): - repo = JsonTokensRepository() +def test_delete_not_found_token(some_tokens_repo): + repo = some_tokens_repo + tokens = repo.get_tokens() input_token = Token( token="imbadtoken", device_name="primary_token", @@ -265,7 +266,7 @@ def test_delete_not_found_token(tokens): with pytest.raises(TokenNotFound): assert repo.delete_token(input_token) is None - # assert read_json(tokens / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT + assert repo.get_tokens() == tokens def test_refresh_token(tokens, mock_token_generate): From fa54220327b416a6a03b6288705ebfdfbc9cbb88 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 15:00:35 +0000 Subject: [PATCH 08/56] test(tokens-repo): agnostic refresh token --- .../test_repository/test_tokens_repository.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 1480b7f..50aeed2 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -269,13 +269,9 @@ def test_delete_not_found_token(some_tokens_repo): assert repo.get_tokens() == tokens -def test_refresh_token(tokens, mock_token_generate): - repo = JsonTokensRepository() - input_token = Token( - token="KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - device_name="primary_token", - created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), - ) +def test_refresh_token(some_tokens_repo, mock_token_generate): + repo = some_tokens_repo + input_token = some_tokens_repo.get_tokens()[0] assert repo.refresh_token(input_token) == Token( token="ZuLNKtnxDeq6w2dpOJhbB3iat_sJLPTPl_rN5uc5MvM", From c86eb8b786cb9975cebdaf586adce5cb347dc7ed Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 15:35:29 +0000 Subject: [PATCH 09/56] test(tokens-repo): agnostic refresh token nonexistent --- tests/test_graphql/test_repository/test_tokens_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 50aeed2..0e25a68 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -280,8 +280,8 @@ def test_refresh_token(some_tokens_repo, mock_token_generate): ) -def test_refresh_not_found_token(tokens, mock_token_generate): - repo = JsonTokensRepository() +def test_refresh_not_found_token(some_tokens_repo, mock_token_generate): + repo = some_tokens_repo input_token = Token( token="idontknowwhoiam", device_name="tellmewhoiam?", From 6f400911fc7239f7a60566ec317dae019cb8b3b5 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 16:12:35 +0000 Subject: [PATCH 10/56] test(tokens-repo): agnostic recovery keys testing --- .../test_repository/test_tokens_repository.py | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 0e25a68..bcd7570 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -143,7 +143,7 @@ def mock_token_generate(mocker): @pytest.fixture def mock_recovery_key_generate(mocker): mock = mocker.patch( - "selfprivacy_api.repositories.tokens.json_tokens_repository.RecoveryKey.generate", + "selfprivacy_api.models.tokens.recovery_key.RecoveryKey.generate", autospec=True, return_value=RecoveryKey( key="889bf49c1d3199d71a2e704718772bd53a422020334db051", @@ -156,7 +156,7 @@ def mock_recovery_key_generate(mocker): @pytest.fixture -def empty_json_repo(tokens): +def empty_json_repo(empty_keys): repo = JsonTokensRepository() for token in repo.get_tokens(): repo.delete_token(token) @@ -297,33 +297,22 @@ def test_refresh_not_found_token(some_tokens_repo, mock_token_generate): ################ -def test_get_recovery_key(tokens): - repo = JsonTokensRepository() - - assert repo.get_recovery_key() == RecoveryKey( - key="ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", - created_at=datetime(2022, 11, 11, 11, 48, 54, 228038), - expires_at=None, - uses_left=2, - ) - - -def test_get_recovery_key_when_empty(empty_keys): - repo = JsonTokensRepository() +def test_get_recovery_key_when_empty(empty_repo): + repo = empty_repo assert repo.get_recovery_key() is None -def test_create_recovery_key(tokens, mock_recovery_key_generate): - repo = JsonTokensRepository() +def test_create_get_recovery_key(some_tokens_repo, mock_recovery_key_generate): + repo = some_tokens_repo assert repo.create_recovery_key(uses_left=1, expiration=None) is not None - # assert read_json(tokens / "tokens.json")["recovery_token"] == { - # "token": "889bf49c1d3199d71a2e704718772bd53a422020334db051", - # "date": "2022-07-15T17:41:31.675698", - # "expiration": None, - # "uses_left": 1, - # } + assert repo.get_recovery_key() == RecoveryKey( + key="889bf49c1d3199d71a2e704718772bd53a422020334db051", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=None, + uses_left=1, + ) def test_use_mnemonic_recovery_key_when_empty( From 732e72d414889b3ff8a192de0637b1c641c652d9 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 16:54:47 +0000 Subject: [PATCH 11/56] test(tokens-repo): mnemonic non-null invalid --- .../test_repository/test_tokens_repository.py | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index bcd7570..6a745be 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -83,11 +83,6 @@ def null_keys(mocker, datadir): return datadir -class RecoveryKeyMockReturnNotValid: - def is_valid() -> bool: - return False - - @pytest.fixture def mock_new_device_key_generate(mocker): mock = mocker.patch( @@ -117,11 +112,16 @@ def mock_generate_token(mocker): @pytest.fixture -def mock_get_recovery_key_return_not_valid(mocker): +def mock_recovery_key_generate_invalid(mocker): mock = mocker.patch( - "selfprivacy_api.repositories.tokens.json_tokens_repository.JsonTokensRepository.get_recovery_key", + "selfprivacy_api.models.tokens.recovery_key.RecoveryKey.generate", autospec=True, - return_value=RecoveryKeyMockReturnNotValid, + return_value=RecoveryKey( + key="889bf49c1d3199d71a2e704718772bd53a422020334db051", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=None, + uses_left=0, + ), ) return mock @@ -315,10 +315,8 @@ def test_create_get_recovery_key(some_tokens_repo, mock_recovery_key_generate): ) -def test_use_mnemonic_recovery_key_when_empty( - empty_keys, mock_recovery_key_generate, mock_token_generate -): - repo = JsonTokensRepository() +def test_use_mnemonic_recovery_key_when_empty(empty_repo): + repo = empty_repo with pytest.raises(RecoveryKeyNotFound): assert ( @@ -331,9 +329,10 @@ def test_use_mnemonic_recovery_key_when_empty( def test_use_mnemonic_not_valid_recovery_key( - tokens, mock_get_recovery_key_return_not_valid + some_tokens_repo, mock_recovery_key_generate_invalid ): - repo = JsonTokensRepository() + repo = some_tokens_repo + assert repo.create_recovery_key(uses_left=0, expiration=None) is not None with pytest.raises(RecoveryKeyNotFound): assert ( @@ -345,8 +344,9 @@ def test_use_mnemonic_not_valid_recovery_key( ) -def test_use_mnemonic_not_mnemonic_recovery_key(tokens): - repo = JsonTokensRepository() +def test_use_mnemonic_not_mnemonic_recovery_key(some_tokens_repo): + repo = some_tokens_repo + assert repo.create_recovery_key(uses_left=1, expiration=None) is not None with pytest.raises(InvalidMnemonic): assert ( @@ -358,8 +358,9 @@ def test_use_mnemonic_not_mnemonic_recovery_key(tokens): ) -def test_use_not_mnemonic_recovery_key(tokens): - repo = JsonTokensRepository() +def test_use_not_mnemonic_recovery_key(some_tokens_repo): + repo = some_tokens_repo + assert repo.create_recovery_key(uses_left=1, expiration=None) is not None with pytest.raises(InvalidMnemonic): assert ( @@ -371,8 +372,9 @@ def test_use_not_mnemonic_recovery_key(tokens): ) -def test_use_not_found_mnemonic_recovery_key(tokens): - repo = JsonTokensRepository() +def test_use_not_found_mnemonic_recovery_key(some_tokens_repo): + repo = some_tokens_repo + assert repo.create_recovery_key(uses_left=1, expiration=None) is not None with pytest.raises(RecoveryKeyNotFound): assert ( @@ -384,8 +386,8 @@ def test_use_not_found_mnemonic_recovery_key(tokens): ) -def test_use_menemonic_recovery_key_when_empty(empty_keys): - repo = JsonTokensRepository() +def test_use_mnemonic_recovery_key_when_empty(empty_repo): + repo = empty_repo with pytest.raises(RecoveryKeyNotFound): assert ( From b9c570720b412f05906fb3ffda449af5f695de39 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 17:07:17 +0000 Subject: [PATCH 12/56] test(tokens-repo): move null recovery token test to json tests Because the null state seems to be specific to json and not reproducible in abstract case. --- .../test_json_tokens_repository.py | 15 +++++++++++++++ .../test_repository/test_tokens_repository.py | 13 ------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository.py b/tests/test_graphql/test_repository/test_json_tokens_repository.py index e90b3dc..a12c0de 100644 --- a/tests/test_graphql/test_repository/test_json_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_json_tokens_repository.py @@ -13,6 +13,7 @@ from datetime import datetime from selfprivacy_api.models.tokens.token import Token from selfprivacy_api.repositories.tokens.exceptions import ( TokenNotFound, + RecoveryKeyNotFound, ) from selfprivacy_api.repositories.tokens.json_tokens_repository import ( JsonTokensRepository, @@ -26,6 +27,7 @@ from test_tokens_repository import ( mock_generate_token, mock_new_device_key_generate, empty_keys, + null_keys, ) @@ -82,6 +84,19 @@ def test_create_recovery_key(tokens, mock_recovery_key_generate): } +def test_use_mnemonic_recovery_key_when_null(null_keys): + repo = JsonTokensRepository() + + with pytest.raises(RecoveryKeyNotFound): + assert ( + repo.use_mnemonic_recovery_key( + mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", + device_name="primary_token", + ) + is None + ) + + def test_use_mnemonic_recovery_key(tokens, mock_generate_token): repo = JsonTokensRepository() diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 6a745be..8fa6c47 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -399,19 +399,6 @@ def test_use_mnemonic_recovery_key_when_empty(empty_repo): ) -def test_use_menemonic_recovery_key_when_null(null_keys): - repo = JsonTokensRepository() - - with pytest.raises(RecoveryKeyNotFound): - assert ( - repo.use_mnemonic_recovery_key( - mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", - device_name="primary_token", - ) - is None - ) - - # agnostic test mixed with an implementation test def test_use_mnemonic_recovery_key(tokens, mock_generate_token): repo = JsonTokensRepository() From dd525fe72377be4197ae64b3c292efa25d64cea0 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 7 Dec 2022 17:30:31 +0000 Subject: [PATCH 13/56] test(tokens-repo): agnostic use recovery token converted json-reading asserts to backend-agnostic asserts --- .../test_repository/test_tokens_repository.py | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 8fa6c47..f7aae99 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -155,6 +155,21 @@ def mock_recovery_key_generate(mocker): return mock +@pytest.fixture +def mock_recovery_key_generate_for_mnemonic(mocker): + mock = mocker.patch( + "selfprivacy_api.models.tokens.recovery_key.RecoveryKey.generate", + autospec=True, + return_value=RecoveryKey( + key="ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=None, + uses_left=1, + ), + ) + return mock + + @pytest.fixture def empty_json_repo(empty_keys): repo = JsonTokensRepository() @@ -400,52 +415,33 @@ def test_use_mnemonic_recovery_key_when_empty(empty_repo): # agnostic test mixed with an implementation test -def test_use_mnemonic_recovery_key(tokens, mock_generate_token): - repo = JsonTokensRepository() +def test_use_mnemonic_recovery_key( + some_tokens_repo, mock_recovery_key_generate_for_mnemonic, mock_generate_token +): + repo = some_tokens_repo + assert repo.create_recovery_key(uses_left=1, expiration=None) is not None - assert repo.use_mnemonic_recovery_key( - mnemonic_phrase="uniform clarify napkin bid dress search input armor police cross salon because myself uphold slice bamboo hungry park", - device_name="newdevice", - ) == Token( + test_token = Token( token="ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", device_name="newdevice", created_at=datetime(2022, 11, 14, 6, 6, 32, 777123), ) - # assert read_json(tokens / "tokens.json")["tokens"] == [ - # { - # "date": "2022-07-15 17:41:31.675698", - # "name": "primary_token", - # "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - # }, - # { - # "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - # "name": "second_token", - # "date": "2022-07-15 17:41:31.675698Z", - # }, - # { - # "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - # "name": "third_token", - # "date": "2022-07-15T17:41:31.675698Z", - # }, - # { - # "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - # "name": "forth_token", - # "date": "2022-07-15T17:41:31.675698", - # }, - # { - # "date": "2022-11-14T06:06:32.777123", - # "name": "newdevice", - # "token": "ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", - # }, - # ] + assert ( + repo.use_mnemonic_recovery_key( + mnemonic_phrase="uniform clarify napkin bid dress search input armor police cross salon because myself uphold slice bamboo hungry park", + device_name="newdevice", + ) + == test_token + ) - # assert read_json(tokens / "tokens.json")["recovery_token"] == { - # "date": "2022-11-11T11:48:54.228038", - # "expiration": None, - # "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", - # "uses_left": 1, - # } + assert test_token in repo.get_tokens() + assert repo.get_recovery_key() == RecoveryKey( + key="ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=None, + uses_left=0, + ) ################## From f96d8b7d7cdd39cda92a1b91dd51bbdf3925c0e9 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 10:06:12 +0000 Subject: [PATCH 14/56] test(tokens-repo): make another mock token generator agnostic --- tests/test_graphql/test_repository/test_tokens_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index f7aae99..2a5b367 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -100,7 +100,7 @@ def mock_new_device_key_generate(mocker): @pytest.fixture def mock_generate_token(mocker): mock = mocker.patch( - "selfprivacy_api.repositories.tokens.json_tokens_repository.Token.generate", + "selfprivacy_api.models.tokens.token.Token.generate", autospec=True, return_value=Token( token="ur71mC4aiI6FIYAN--cTL-38rPHS5D6NuB1bgN_qKF4", From dc778b545e9cec45325e635378e89084b242cda1 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 10:12:52 +0000 Subject: [PATCH 15/56] test(tokens-repo): get new device key --- .../test_repository/test_tokens_repository.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 2a5b367..118162d 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -86,7 +86,7 @@ def null_keys(mocker, datadir): @pytest.fixture def mock_new_device_key_generate(mocker): mock = mocker.patch( - "selfprivacy_api.repositories.tokens.json_tokens_repository.NewDeviceKey.generate", + "selfprivacy_api.models.tokens.new_device_key.NewDeviceKey.generate", autospec=True, return_value=NewDeviceKey( key="43478d05b35e4781598acd76e33832bb", @@ -449,15 +449,14 @@ def test_use_mnemonic_recovery_key( ################## -def test_get_new_device_key(tokens, mock_new_device_key_generate): - repo = JsonTokensRepository() +def test_get_new_device_key(some_tokens_repo, mock_new_device_key_generate): + repo = some_tokens_repo - assert repo.get_new_device_key() is not None - # assert read_json(tokens / "tokens.json")["new_device"] == { - # "date": "2022-07-15T17:41:31.675698", - # "expiration": "2022-07-15T17:41:31.675698", - # "token": "43478d05b35e4781598acd76e33832bb", - # } + assert repo.get_new_device_key() == NewDeviceKey( + key="43478d05b35e4781598acd76e33832bb", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + ) def test_delete_new_device_key(tokens): From 73584872f053a36ae5abacc15b7cd623e75d7506 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 10:41:22 +0000 Subject: [PATCH 16/56] test(tokens-repo): agnosticise simple new device key tests the state of json file is tested separately in test_json_tokens_repository.py --- .../test_repository/test_tokens_repository.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 118162d..2c74a47 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -206,6 +206,7 @@ def some_tokens_repo(empty_repo): assert len(empty_repo.get_tokens()) == len(ORIGINAL_DEVICE_NAMES) for i, t in enumerate(empty_repo.get_tokens()): assert t.device_name == ORIGINAL_DEVICE_NAMES[i] + assert empty_repo.get_new_device_key() is not None return empty_repo @@ -459,18 +460,17 @@ def test_get_new_device_key(some_tokens_repo, mock_new_device_key_generate): ) -def test_delete_new_device_key(tokens): - repo = JsonTokensRepository() +def test_delete_new_device_key(some_tokens_repo): + repo = some_tokens_repo assert repo.delete_new_device_key() is None - # assert "new_device" not in read_json(tokens / "tokens.json") + # we cannot say if there is ot not without creating it? -def test_delete_new_device_key_when_empty(empty_keys): - repo = JsonTokensRepository() +def test_delete_new_device_key_when_empty(empty_repo): + repo = empty_repo - repo.delete_new_device_key() - # assert "new_device" not in read_json(empty_keys / "empty_keys.json") + assert repo.delete_new_device_key() is None def test_use_invalid_mnemonic_new_device_key( From 3feebd5290c5b5e7788a4a5b8db015aea46eef03 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 10:51:32 +0000 Subject: [PATCH 17/56] test(tokens-repo): invalid mnemonic new device key --- .../test_graphql/test_repository/test_tokens_repository.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 2c74a47..28741ef 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -473,10 +473,8 @@ def test_delete_new_device_key_when_empty(empty_repo): assert repo.delete_new_device_key() is None -def test_use_invalid_mnemonic_new_device_key( - tokens, mock_new_device_key_generate, datadir, mock_token_generate -): - repo = JsonTokensRepository() +def test_use_invalid_mnemonic_new_device_key(some_tokens_repo): + repo = some_tokens_repo with pytest.raises(InvalidMnemonic): assert ( From cf7b7eb8a70f7d471b2032d00411cf0a33249c36 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 10:57:11 +0000 Subject: [PATCH 18/56] test(tokens-repo): notfound mnemonic new device key --- tests/test_graphql/test_repository/test_tokens_repository.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 28741ef..6619e1f 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -487,9 +487,10 @@ def test_use_invalid_mnemonic_new_device_key(some_tokens_repo): def test_use_not_exists_mnemonic_new_device_key( - tokens, mock_new_device_key_generate, mock_token_generate + empty_repo, mock_new_device_key_generate ): - repo = JsonTokensRepository() + repo = empty_repo + assert repo.get_new_device_key() is not None with pytest.raises(NewDeviceKeyNotFound): assert ( From ce411e92912df70771f3ae68cf78ebfacb396200 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 11:28:07 +0000 Subject: [PATCH 19/56] test(tokens-repo): using a mnemonic device key --- .../test_repository/test_tokens_repository.py | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 6619e1f..677170b 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -97,6 +97,21 @@ def mock_new_device_key_generate(mocker): return mock +# mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", +@pytest.fixture +def mock_new_device_key_generate_for_mnemonic(mocker): + mock = mocker.patch( + "selfprivacy_api.models.tokens.new_device_key.NewDeviceKey.generate", + autospec=True, + return_value=NewDeviceKey( + key="2237238de23dc71ab558e317bdb8ff8e", + created_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + expires_at=datetime(2022, 7, 15, 17, 41, 31, 675698), + ), + ) + return mock + + @pytest.fixture def mock_generate_token(mocker): mock = mocker.patch( @@ -503,9 +518,10 @@ def test_use_not_exists_mnemonic_new_device_key( def test_use_mnemonic_new_device_key( - tokens, mock_new_device_key_generate, mock_token_generate + empty_repo, mock_new_device_key_generate_for_mnemonic ): - repo = JsonTokensRepository() + repo = empty_repo + assert repo.get_new_device_key() is not None assert ( repo.use_mnemonic_new_device_key( @@ -514,7 +530,16 @@ def test_use_mnemonic_new_device_key( ) is not None ) - # assert read_json(datadir / "tokens.json")["new_device"] == [] + + # we must delete the key after use + with pytest.raises(NewDeviceKeyNotFound): + assert ( + repo.use_mnemonic_new_device_key( + device_name="imnew", + mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", + ) + is None + ) def test_use_mnemonic_new_device_key_when_empty(empty_keys): From be13d6163e4eac35ae526e1de0ad714d883fac9d Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 11:30:33 +0000 Subject: [PATCH 20/56] test(tokens-repo): use a mnemonic device key on an empty repo --- tests/test_graphql/test_repository/test_tokens_repository.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 677170b..3bd35b9 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -542,8 +542,8 @@ def test_use_mnemonic_new_device_key( ) -def test_use_mnemonic_new_device_key_when_empty(empty_keys): - repo = JsonTokensRepository() +def test_use_mnemonic_new_device_key_when_empty(empty_repo): + repo = empty_repo with pytest.raises(NewDeviceKeyNotFound): assert ( From 84bfa333fa9cf2283bfd524e76f225f69098ecd9 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 11:35:29 +0000 Subject: [PATCH 21/56] test(tokens-repo): move new device key null test to json tests --- .../test_repository/test_json_tokens_repository.py | 14 ++++++++++++++ .../test_repository/test_tokens_repository.py | 13 ------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository.py b/tests/test_graphql/test_repository/test_json_tokens_repository.py index a12c0de..feb3b50 100644 --- a/tests/test_graphql/test_repository/test_json_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_json_tokens_repository.py @@ -14,6 +14,7 @@ from selfprivacy_api.models.tokens.token import Token from selfprivacy_api.repositories.tokens.exceptions import ( TokenNotFound, RecoveryKeyNotFound, + NewDeviceKeyNotFound, ) from selfprivacy_api.repositories.tokens.json_tokens_repository import ( JsonTokensRepository, @@ -167,3 +168,16 @@ def test_delete_new_device_key_when_empty(empty_keys): repo.delete_new_device_key() assert "new_device" not in read_json(empty_keys / "empty_keys.json") + + +def test_use_mnemonic_new_device_key_when_null(null_keys): + repo = JsonTokensRepository() + + with pytest.raises(NewDeviceKeyNotFound): + assert ( + repo.use_mnemonic_new_device_key( + device_name="imnew", + mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", + ) + is None + ) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 3bd35b9..c16f5a3 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -553,16 +553,3 @@ def test_use_mnemonic_new_device_key_when_empty(empty_repo): ) is None ) - - -def test_use_mnemonic_new_device_key_when_null(null_keys): - repo = JsonTokensRepository() - - with pytest.raises(NewDeviceKeyNotFound): - assert ( - repo.use_mnemonic_new_device_key( - device_name="imnew", - mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", - ) - is None - ) From 4492bbe99599149c1f248fb3ddfe76db9aecadac Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 11:48:11 +0000 Subject: [PATCH 22/56] test(tokens-repo): move null keys and tokens fixtures to json tests and remove corresponding json files from the folder --- .../test_json_tokens_repository.py | 17 +++++++-- .../test_repository/test_tokens_repository.py | 15 -------- .../test_tokens_repository/null_keys.json | 26 -------------- .../test_tokens_repository/tokens.json | 35 ------------------- 4 files changed, 15 insertions(+), 78 deletions(-) delete mode 100644 tests/test_graphql/test_repository/test_tokens_repository/null_keys.json delete mode 100644 tests/test_graphql/test_repository/test_tokens_repository/tokens.json diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository.py b/tests/test_graphql/test_repository/test_json_tokens_repository.py index feb3b50..ac4a3d0 100644 --- a/tests/test_graphql/test_repository/test_json_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_json_tokens_repository.py @@ -23,15 +23,28 @@ from tests.common import read_json from test_tokens_repository import ORIGINAL_TOKEN_CONTENT from test_tokens_repository import ( - tokens, mock_recovery_key_generate, mock_generate_token, mock_new_device_key_generate, empty_keys, - null_keys, ) +@pytest.fixture +def tokens(mocker, datadir): + mocker.patch("selfprivacy_api.utils.TOKENS_FILE", new=datadir / "tokens.json") + assert read_json(datadir / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT + return datadir + + +@pytest.fixture +def null_keys(mocker, datadir): + mocker.patch("selfprivacy_api.utils.TOKENS_FILE", new=datadir / "null_keys.json") + assert read_json(datadir / "null_keys.json")["recovery_token"] is None + assert read_json(datadir / "null_keys.json")["new_device"] is None + return datadir + + def test_delete_token(tokens): repo = JsonTokensRepository() input_token = Token( diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index c16f5a3..a7affd6 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -55,13 +55,6 @@ ORIGINAL_DEVICE_NAMES = [ ] -@pytest.fixture -def tokens(mocker, datadir): - mocker.patch("selfprivacy_api.utils.TOKENS_FILE", new=datadir / "tokens.json") - assert read_json(datadir / "tokens.json")["tokens"] == ORIGINAL_TOKEN_CONTENT - return datadir - - @pytest.fixture def empty_keys(mocker, datadir): mocker.patch("selfprivacy_api.utils.TOKENS_FILE", new=datadir / "empty_keys.json") @@ -75,14 +68,6 @@ def empty_keys(mocker, datadir): return datadir -@pytest.fixture -def null_keys(mocker, datadir): - mocker.patch("selfprivacy_api.utils.TOKENS_FILE", new=datadir / "null_keys.json") - assert read_json(datadir / "null_keys.json")["recovery_token"] is None - assert read_json(datadir / "null_keys.json")["new_device"] is None - return datadir - - @pytest.fixture def mock_new_device_key_generate(mocker): mock = mocker.patch( diff --git a/tests/test_graphql/test_repository/test_tokens_repository/null_keys.json b/tests/test_graphql/test_repository/test_tokens_repository/null_keys.json deleted file mode 100644 index 45e6f90..0000000 --- a/tests/test_graphql/test_repository/test_tokens_repository/null_keys.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tokens": [ - { - "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - "name": "primary_token", - "date": "2022-07-15 17:41:31.675698" - }, - { - "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - "name": "second_token", - "date": "2022-07-15 17:41:31.675698Z" - }, - { - "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - "name": "third_token", - "date": "2022-07-15T17:41:31.675698Z" - }, - { - "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - "name": "forth_token", - "date": "2022-07-15T17:41:31.675698" - } - ], - "recovery_token": null, - "new_device": null -} diff --git a/tests/test_graphql/test_repository/test_tokens_repository/tokens.json b/tests/test_graphql/test_repository/test_tokens_repository/tokens.json deleted file mode 100644 index bb1805c..0000000 --- a/tests/test_graphql/test_repository/test_tokens_repository/tokens.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "tokens": [ - { - "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - "name": "primary_token", - "date": "2022-07-15 17:41:31.675698" - }, - { - "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - "name": "second_token", - "date": "2022-07-15 17:41:31.675698Z" - }, - { - "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - "name": "third_token", - "date": "2022-07-15T17:41:31.675698Z" - }, - { - "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - "name": "forth_token", - "date": "2022-07-15T17:41:31.675698" - } - ], - "recovery_token": { - "token": "ed653e4b8b042b841d285fa7a682fa09e925ddb2d8906f54", - "date": "2022-11-11T11:48:54.228038", - "expiration": null, - "uses_left": 2 - }, - "new_device": { - "token": "2237238de23dc71ab558e317bdb8ff8e", - "date": "2022-10-26 20:50:47.973212", - "expiration": "2022-10-26 21:00:47.974153" - } -} From c12dca9d9b15b2a55ec6919d3926c3de92c4b065 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 11:56:47 +0000 Subject: [PATCH 23/56] refactor(tokens-repo): delete unused timezone import --- tests/test_graphql/test_repository/test_tokens_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index a7affd6..40d3e7e 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -2,7 +2,7 @@ # pylint: disable=unused-argument # pylint: disable=missing-function-docstring -from datetime import datetime, timezone +from datetime import datetime import pytest From 0d748d7ab13e53590c1eace07eb4173c247a273d Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 12:23:36 +0000 Subject: [PATCH 24/56] test(tokens-repo): move original token content to json tests --- .../test_json_tokens_repository.py | 26 +++++++++++++++++-- .../test_repository/test_tokens_repository.py | 23 ---------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/tests/test_graphql/test_repository/test_json_tokens_repository.py b/tests/test_graphql/test_repository/test_json_tokens_repository.py index ac4a3d0..af8c844 100644 --- a/tests/test_graphql/test_repository/test_json_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_json_tokens_repository.py @@ -19,9 +19,8 @@ from selfprivacy_api.repositories.tokens.exceptions import ( from selfprivacy_api.repositories.tokens.json_tokens_repository import ( JsonTokensRepository, ) -from tests.common import read_json -from test_tokens_repository import ORIGINAL_TOKEN_CONTENT +from tests.common import read_json from test_tokens_repository import ( mock_recovery_key_generate, mock_generate_token, @@ -29,6 +28,29 @@ from test_tokens_repository import ( empty_keys, ) +ORIGINAL_TOKEN_CONTENT = [ + { + "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", + "name": "primary_token", + "date": "2022-07-15 17:41:31.675698", + }, + { + "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", + "name": "second_token", + "date": "2022-07-15 17:41:31.675698Z", + }, + { + "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", + "name": "third_token", + "date": "2022-07-15T17:41:31.675698Z", + }, + { + "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", + "name": "forth_token", + "date": "2022-07-15T17:41:31.675698", + }, +] + @pytest.fixture def tokens(mocker, datadir): diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 40d3e7e..0372b92 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -24,29 +24,6 @@ from selfprivacy_api.repositories.tokens.redis_tokens_repository import ( from tests.common import read_json -ORIGINAL_TOKEN_CONTENT = [ - { - "token": "KG9ni-B-CMPk327Zv1qC7YBQaUGaBUcgdkvMvQ2atFI", - "name": "primary_token", - "date": "2022-07-15 17:41:31.675698", - }, - { - "token": "3JKgLOtFu6ZHgE4OU-R-VdW47IKpg-YQL0c6n7bol68", - "name": "second_token", - "date": "2022-07-15 17:41:31.675698Z", - }, - { - "token": "LYiwFDekvALKTQSjk7vtMQuNP_6wqKuV-9AyMKytI_8", - "name": "third_token", - "date": "2022-07-15T17:41:31.675698Z", - }, - { - "token": "dD3CFPcEZvapscgzWb7JZTLog7OMkP7NzJeu2fAazXM", - "name": "forth_token", - "date": "2022-07-15T17:41:31.675698", - }, -] - ORIGINAL_DEVICE_NAMES = [ "primary_token", "second_token", From b856a2aad3b533301965958d6f7a786ea7f487cb Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Fri, 9 Dec 2022 12:25:20 +0000 Subject: [PATCH 25/56] test(tokens-repo): re-add delete token test --- .../test_repository/test_tokens_repository.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 0372b92..df56444 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -248,6 +248,20 @@ def test_create_token(empty_repo, mock_token_generate): ] +def test_delete_token(some_tokens_repo): + repo = some_tokens_repo + original_tokens = repo.get_tokens() + input_token = original_tokens[1] + + repo.delete_token(input_token) + + tokens_after_delete = repo.get_tokens() + for token in original_tokens: + if token != input_token: + assert token in tokens_after_delete + assert len(original_tokens) == len(tokens_after_delete) + 1 + + def test_delete_not_found_token(some_tokens_repo): repo = some_tokens_repo tokens = repo.get_tokens() From ff264ec808f18509d1138f756911f6a241a80391 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 10:06:31 +0000 Subject: [PATCH 26/56] refactor(tokens-repo): simplify getting tokens get_token_by_token_string and get_token_by_name are no longer tied to json. --- .../tokens/json_tokens_repository.py | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index aad3158..30d8021 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -25,29 +25,19 @@ DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" class JsonTokensRepository(AbstractTokensRepository): def get_token_by_token_string(self, token_string: str) -> Optional[Token]: """Get the token by token""" - with ReadUserData(UserDataFiles.TOKENS) as tokens_file: - for userdata_token in tokens_file["tokens"]: - if userdata_token["token"] == token_string: - - return Token( - token=token_string, - device_name=userdata_token["name"], - created_at=userdata_token["date"], - ) + tokens = self.get_tokens() + for token in tokens: + if token.token == token_string: + return token raise TokenNotFound("Token not found!") def get_token_by_name(self, token_name: str) -> Optional[Token]: """Get the token by name""" - with ReadUserData(UserDataFiles.TOKENS) as tokens_file: - for userdata_token in tokens_file["tokens"]: - if userdata_token["name"] == token_name: - - return Token( - token=userdata_token["token"], - device_name=token_name, - created_at=userdata_token["date"], - ) + tokens = self.get_tokens() + for token in tokens: + if token.device_name == token_name: + return token raise TokenNotFound("Token not found!") From 4e60d1d37ac14040ebb170743a672164018d3fc4 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 10:15:33 +0000 Subject: [PATCH 27/56] refactor(tokens-repo): move token getters to abstract class Not performance-optimal, but not in critical path either. 100 tokens max irl? --- .../tokens/abstract_tokens_repository.py | 15 +++++++++++++-- .../tokens/json_tokens_repository.py | 18 ------------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py index 3cf6e1d..2840917 100644 --- a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py @@ -3,18 +3,29 @@ from datetime import datetime from typing import Optional from selfprivacy_api.models.tokens.token import Token +from selfprivacy_api.repositories.tokens.exceptions import TokenNotFound from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey class AbstractTokensRepository(ABC): - @abstractmethod def get_token_by_token_string(self, token_string: str) -> Optional[Token]: """Get the token by token""" + tokens = self.get_tokens() + for token in tokens: + if token.token == token_string: + return token + + raise TokenNotFound("Token not found!") - @abstractmethod def get_token_by_name(self, token_name: str) -> Optional[Token]: """Get the token by name""" + tokens = self.get_tokens() + for token in tokens: + if token.device_name == token_name: + return token + + raise TokenNotFound("Token not found!") @abstractmethod def get_tokens(self) -> list[Token]: diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 30d8021..86e756a 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -23,24 +23,6 @@ DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" class JsonTokensRepository(AbstractTokensRepository): - def get_token_by_token_string(self, token_string: str) -> Optional[Token]: - """Get the token by token""" - tokens = self.get_tokens() - for token in tokens: - if token.token == token_string: - return token - - raise TokenNotFound("Token not found!") - - def get_token_by_name(self, token_name: str) -> Optional[Token]: - """Get the token by name""" - tokens = self.get_tokens() - for token in tokens: - if token.device_name == token_name: - return token - - raise TokenNotFound("Token not found!") - def get_tokens(self) -> list[Token]: """Get the tokens""" tokens_list = [] From 27a7c24bc35f415977c611c1e00a2909d4c5cbc5 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 10:42:16 +0000 Subject: [PATCH 28/56] refactor(tokens-repo): separate token storing --- .../repositories/tokens/json_tokens_repository.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 86e756a..e011b62 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -43,6 +43,11 @@ class JsonTokensRepository(AbstractTokensRepository): """Create new token""" new_token = Token.generate(device_name) + self.__store_token(new_token) + + return new_token + + def __store_token(self, new_token: Token): with WriteUserData(UserDataFiles.TOKENS) as tokens_file: tokens_file["tokens"].append( { @@ -51,7 +56,6 @@ class JsonTokensRepository(AbstractTokensRepository): "date": new_token.created_at.strftime(DATETIME_FORMAT), } ) - return new_token def delete_token(self, input_token: Token) -> None: """Delete the token""" From 572ec75c39db21c05282bde443276092b14a5f0a Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 11:15:25 +0000 Subject: [PATCH 29/56] refactor(tokens-repo): rewrite token refresh now it is not json-dependent. --- .../repositories/tokens/json_tokens_repository.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index e011b62..2f4a0b1 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -71,16 +71,10 @@ class JsonTokensRepository(AbstractTokensRepository): """Change the token field of the existing token""" new_token = Token.generate(device_name=input_token.device_name) - with WriteUserData(UserDataFiles.TOKENS) as tokens_file: - for userdata_token in tokens_file["tokens"]: - - if userdata_token["name"] == input_token.device_name: - userdata_token["token"] = new_token.token - userdata_token["date"] = ( - new_token.created_at.strftime(DATETIME_FORMAT), - ) - - return new_token + if input_token in self.get_tokens(): + self.delete_token(input_token) + self.__store_token(new_token) + return new_token raise TokenNotFound("Token not found!") From 682cd4ae87ad73cb3a23655bec6cbd29ed66d606 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 11:50:04 +0000 Subject: [PATCH 30/56] refactor(tokens-repo): move create_token to abstract class --- .../tokens/abstract_tokens_repository.py | 10 +++++++++- .../repositories/tokens/json_tokens_repository.py | 13 +++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py index 2840917..bb77c1d 100644 --- a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py @@ -31,9 +31,13 @@ class AbstractTokensRepository(ABC): def get_tokens(self) -> list[Token]: """Get the tokens""" - @abstractmethod def create_token(self, device_name: str) -> Token: """Create new token""" + new_token = Token.generate(device_name) + + self._store_token(new_token) + + return new_token @abstractmethod def delete_token(self, input_token: Token) -> None: @@ -102,3 +106,7 @@ class AbstractTokensRepository(ABC): self, mnemonic_phrase: str, device_name: str ) -> Token: """Use the mnemonic new device key""" + + @abstractmethod + def _store_token(self, new_token: Token): + """Store a token directly""" diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 2f4a0b1..963e3a9 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -39,15 +39,8 @@ class JsonTokensRepository(AbstractTokensRepository): return tokens_list - def create_token(self, device_name: str) -> Token: - """Create new token""" - new_token = Token.generate(device_name) - - self.__store_token(new_token) - - return new_token - - def __store_token(self, new_token: Token): + def _store_token(self, new_token: Token): + """Store a token directly""" with WriteUserData(UserDataFiles.TOKENS) as tokens_file: tokens_file["tokens"].append( { @@ -73,7 +66,7 @@ class JsonTokensRepository(AbstractTokensRepository): if input_token in self.get_tokens(): self.delete_token(input_token) - self.__store_token(new_token) + self._store_token(new_token) return new_token raise TokenNotFound("Token not found!") From 9a49067e53b1c7c9634ebcb0fec622fede60c725 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 11:55:49 +0000 Subject: [PATCH 31/56] refactor(tokens-repo): move token refreshing to parent class --- .../repositories/tokens/abstract_tokens_repository.py | 11 +++++++++-- .../repositories/tokens/json_tokens_repository.py | 11 ----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py index bb77c1d..29c96a4 100644 --- a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py @@ -43,9 +43,16 @@ class AbstractTokensRepository(ABC): def delete_token(self, input_token: Token) -> None: """Delete the token""" - @abstractmethod def refresh_token(self, input_token: Token) -> Token: - """Refresh the token""" + """Change the token field of the existing token""" + new_token = Token.generate(device_name=input_token.device_name) + + if input_token in self.get_tokens(): + self.delete_token(input_token) + self._store_token(new_token) + return new_token + + raise TokenNotFound("Token not found!") def is_token_valid(self, token_string: str) -> bool: """Check if the token is valid""" diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 963e3a9..c7d716f 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -60,17 +60,6 @@ class JsonTokensRepository(AbstractTokensRepository): raise TokenNotFound("Token not found!") - def refresh_token(self, input_token: Token) -> Token: - """Change the token field of the existing token""" - new_token = Token.generate(device_name=input_token.device_name) - - if input_token in self.get_tokens(): - self.delete_token(input_token) - self._store_token(new_token) - return new_token - - raise TokenNotFound("Token not found!") - def get_recovery_key(self) -> Optional[RecoveryKey]: """Get the recovery key""" with ReadUserData(UserDataFiles.TOKENS) as tokens_file: From 671203e99043fe3eeb478ff07c67bb75814a3534 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 14:06:24 +0000 Subject: [PATCH 32/56] refactor(tokens-repository): dissect use_mnemonic_recovery_key() --- .../tokens/json_tokens_repository.py | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index c7d716f..1ebdca8 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -102,41 +102,31 @@ class JsonTokensRepository(AbstractTokensRepository): self, mnemonic_phrase: str, device_name: str ) -> Token: """Use the mnemonic recovery key and create a new token with the given name""" - recovery_key = self.get_recovery_key() - - if recovery_key is None: + if not self.is_recovery_key_valid(): raise RecoveryKeyNotFound("Recovery key not found") - if not recovery_key.is_valid(): + recovery_hex_key = self.get_recovery_key().key + if not self._assert_mnemonic(recovery_hex_key, mnemonic_phrase): raise RecoveryKeyNotFound("Recovery key not found") - recovery_token = bytes.fromhex(recovery_key.key) + new_token = self.create_token(device_name=device_name) + self._decrement_recovery_token() + + return new_token + + def _decrement_recovery_token(self): + if self.is_recovery_key_valid(): + with WriteUserData(UserDataFiles.TOKENS) as tokens: + tokens["recovery_token"]["uses_left"] -= 1 + + def _assert_mnemonic(self, hex_key: str, mnemonic_phrase: str): + recovery_token = bytes.fromhex(hex_key) if not Mnemonic(language="english").check(mnemonic_phrase): raise InvalidMnemonic("Phrase is not mnemonic!") phrase_bytes = Mnemonic(language="english").to_entropy(mnemonic_phrase) - if phrase_bytes != recovery_token: - raise RecoveryKeyNotFound("Recovery key not found") - - new_token = Token.generate(device_name=device_name) - - with WriteUserData(UserDataFiles.TOKENS) as tokens: - tokens["tokens"].append( - { - "token": new_token.token, - "name": new_token.device_name, - "date": new_token.created_at.strftime(DATETIME_FORMAT), - } - ) - - if "recovery_token" in tokens: - if ( - "uses_left" in tokens["recovery_token"] - and tokens["recovery_token"]["uses_left"] is not None - ): - tokens["recovery_token"]["uses_left"] -= 1 - return new_token + return phrase_bytes == recovery_token def get_new_device_key(self) -> NewDeviceKey: """Creates and returns the new device key""" From 772c0dfc64f149f94a1a90352b3286af75333a36 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 14:22:36 +0000 Subject: [PATCH 33/56] refactor(tokens-repository): move use_mnemonic_recovery_key() to abstract class --- .../tokens/abstract_tokens_repository.py | 35 +++++++++++++++++-- .../tokens/json_tokens_repository.py | 27 +------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py index 29c96a4..82a0189 100644 --- a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py @@ -1,9 +1,14 @@ from abc import ABC, abstractmethod from datetime import datetime from typing import Optional +from mnemonic import Mnemonic from selfprivacy_api.models.tokens.token import Token -from selfprivacy_api.repositories.tokens.exceptions import TokenNotFound +from selfprivacy_api.repositories.tokens.exceptions import ( + TokenNotFound, + InvalidMnemonic, + RecoveryKeyNotFound, +) from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey @@ -87,11 +92,22 @@ class AbstractTokensRepository(ABC): ) -> RecoveryKey: """Create the recovery key""" - @abstractmethod def use_mnemonic_recovery_key( self, mnemonic_phrase: str, device_name: str ) -> Token: """Use the mnemonic recovery key and create a new token with the given name""" + if not self.is_recovery_key_valid(): + raise RecoveryKeyNotFound("Recovery key not found") + + recovery_hex_key = self.get_recovery_key().key + if not self._assert_mnemonic(recovery_hex_key, mnemonic_phrase): + raise RecoveryKeyNotFound("Recovery key not found") + + new_token = self.create_token(device_name=device_name) + + self._decrement_recovery_token() + + return new_token def is_recovery_key_valid(self) -> bool: """Check if the recovery key is valid""" @@ -117,3 +133,18 @@ class AbstractTokensRepository(ABC): @abstractmethod def _store_token(self, new_token: Token): """Store a token directly""" + + @abstractmethod + def _decrement_recovery_token(self): + """Decrement recovery key use count by one""" + + # TODO: find a proper place for it + def _assert_mnemonic(self, hex_key: str, mnemonic_phrase: str): + """Return true if hex string matches the phrase, false otherwise + Raise an InvalidMnemonic error if not mnemonic""" + recovery_token = bytes.fromhex(hex_key) + if not Mnemonic(language="english").check(mnemonic_phrase): + raise InvalidMnemonic("Phrase is not mnemonic!") + + phrase_bytes = Mnemonic(language="english").to_entropy(mnemonic_phrase) + return phrase_bytes == recovery_token diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 1ebdca8..50d8869 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -11,7 +11,6 @@ from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey from selfprivacy_api.repositories.tokens.exceptions import ( TokenNotFound, - RecoveryKeyNotFound, InvalidMnemonic, NewDeviceKeyNotFound, ) @@ -98,36 +97,12 @@ class JsonTokensRepository(AbstractTokensRepository): return recovery_key - def use_mnemonic_recovery_key( - self, mnemonic_phrase: str, device_name: str - ) -> Token: - """Use the mnemonic recovery key and create a new token with the given name""" - if not self.is_recovery_key_valid(): - raise RecoveryKeyNotFound("Recovery key not found") - - recovery_hex_key = self.get_recovery_key().key - if not self._assert_mnemonic(recovery_hex_key, mnemonic_phrase): - raise RecoveryKeyNotFound("Recovery key not found") - - new_token = self.create_token(device_name=device_name) - - self._decrement_recovery_token() - - return new_token - def _decrement_recovery_token(self): + """Decrement recovery key use count by one""" if self.is_recovery_key_valid(): with WriteUserData(UserDataFiles.TOKENS) as tokens: tokens["recovery_token"]["uses_left"] -= 1 - def _assert_mnemonic(self, hex_key: str, mnemonic_phrase: str): - recovery_token = bytes.fromhex(hex_key) - if not Mnemonic(language="english").check(mnemonic_phrase): - raise InvalidMnemonic("Phrase is not mnemonic!") - - phrase_bytes = Mnemonic(language="english").to_entropy(mnemonic_phrase) - return phrase_bytes == recovery_token - def get_new_device_key(self) -> NewDeviceKey: """Creates and returns the new device key""" new_device_key = NewDeviceKey.generate() From 4498003aca13019f59fd058f46b9439c61f9cb88 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 15:19:00 +0000 Subject: [PATCH 34/56] refactor(tokens-repository): dissect use_mnemonic_new_device_key() --- .../tokens/json_tokens_repository.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 50d8869..55316d6 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -123,33 +123,31 @@ class JsonTokensRepository(AbstractTokensRepository): del tokens_file["new_device"] return - def use_mnemonic_new_device_key( - self, mnemonic_phrase: str, device_name: str - ) -> Token: - """Use the mnemonic new device key""" - + def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: + """Retrieves new device key that is already stored.""" with ReadUserData(UserDataFiles.TOKENS) as tokens_file: if "new_device" not in tokens_file or tokens_file["new_device"] is None: - raise NewDeviceKeyNotFound("New device key not found") + return new_device_key = NewDeviceKey( key=tokens_file["new_device"]["token"], created_at=tokens_file["new_device"]["date"], expires_at=tokens_file["new_device"]["expiration"], ) + return new_device_key - token = bytes.fromhex(new_device_key.key) + def use_mnemonic_new_device_key( + self, mnemonic_phrase: str, device_name: str + ) -> Token: + """Use the mnemonic new device key""" + new_device_key = self._get_stored_new_device_key() + if not new_device_key: + raise NewDeviceKeyNotFound - if not Mnemonic(language="english").check(mnemonic_phrase): - raise InvalidMnemonic("Phrase is not mnemonic!") - - phrase_bytes = Mnemonic(language="english").to_entropy(mnemonic_phrase) - if bytes(phrase_bytes) != bytes(token): + if not self._assert_mnemonic(new_device_key.key, mnemonic_phrase): raise NewDeviceKeyNotFound("Phrase is not token!") new_token = Token.generate(device_name=device_name) - with WriteUserData(UserDataFiles.TOKENS) as tokens: - if "new_device" in tokens: - del tokens["new_device"] + self.delete_new_device_key() return new_token From 2797c6f88f4309b72695e86409028bf335129bb9 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 15:36:38 +0000 Subject: [PATCH 35/56] fix(tokens-repository): use_mnemonic_new_device_key() now stores a token --- .../repositories/tokens/json_tokens_repository.py | 2 +- .../test_repository/test_tokens_repository.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 55316d6..12826ba 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -147,7 +147,7 @@ class JsonTokensRepository(AbstractTokensRepository): if not self._assert_mnemonic(new_device_key.key, mnemonic_phrase): raise NewDeviceKeyNotFound("Phrase is not token!") - new_token = Token.generate(device_name=device_name) + new_token = self.create_token(device_name=device_name) self.delete_new_device_key() return new_token diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index df56444..bdad6d8 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -499,14 +499,14 @@ def test_use_mnemonic_new_device_key( repo = empty_repo assert repo.get_new_device_key() is not None - assert ( - repo.use_mnemonic_new_device_key( - device_name="imnew", - mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", - ) - is not None + new_token = repo.use_mnemonic_new_device_key( + device_name="imnew", + mnemonic_phrase="captain ribbon toddler settle symbol minute step broccoli bless universe divide bulb", ) + assert new_token.device_name == "imnew" + assert new_token in repo.get_tokens() + # we must delete the key after use with pytest.raises(NewDeviceKeyNotFound): assert ( From ca822cdf6fe1b4e84c2c33d940044b2cb6db3318 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Mon, 12 Dec 2022 15:43:58 +0000 Subject: [PATCH 36/56] refactor(tokens-repository): move use_mnemonic_new_device_key() to abstract class --- .../tokens/abstract_tokens_repository.py | 17 ++++++++++++++++- .../tokens/json_tokens_repository.py | 19 ------------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py index 82a0189..a67d62d 100644 --- a/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/abstract_tokens_repository.py @@ -8,6 +8,7 @@ from selfprivacy_api.repositories.tokens.exceptions import ( TokenNotFound, InvalidMnemonic, RecoveryKeyNotFound, + NewDeviceKeyNotFound, ) from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey @@ -124,11 +125,21 @@ class AbstractTokensRepository(ABC): def delete_new_device_key(self) -> None: """Delete the new device key""" - @abstractmethod def use_mnemonic_new_device_key( self, mnemonic_phrase: str, device_name: str ) -> Token: """Use the mnemonic new device key""" + new_device_key = self._get_stored_new_device_key() + if not new_device_key: + raise NewDeviceKeyNotFound + + if not self._assert_mnemonic(new_device_key.key, mnemonic_phrase): + raise NewDeviceKeyNotFound("Phrase is not token!") + + new_token = self.create_token(device_name=device_name) + self.delete_new_device_key() + + return new_token @abstractmethod def _store_token(self, new_token: Token): @@ -138,6 +149,10 @@ class AbstractTokensRepository(ABC): def _decrement_recovery_token(self): """Decrement recovery key use count by one""" + @abstractmethod + def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: + """Retrieves new device key that is already stored.""" + # TODO: find a proper place for it def _assert_mnemonic(self, hex_key: str, mnemonic_phrase: str): """Return true if hex string matches the phrase, false otherwise diff --git a/selfprivacy_api/repositories/tokens/json_tokens_repository.py b/selfprivacy_api/repositories/tokens/json_tokens_repository.py index 12826ba..b4c0ab2 100644 --- a/selfprivacy_api/repositories/tokens/json_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/json_tokens_repository.py @@ -3,7 +3,6 @@ temporary legacy """ from typing import Optional from datetime import datetime -from mnemonic import Mnemonic from selfprivacy_api.utils import UserDataFiles, WriteUserData, ReadUserData from selfprivacy_api.models.tokens.token import Token @@ -11,8 +10,6 @@ from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey from selfprivacy_api.repositories.tokens.exceptions import ( TokenNotFound, - InvalidMnemonic, - NewDeviceKeyNotFound, ) from selfprivacy_api.repositories.tokens.abstract_tokens_repository import ( AbstractTokensRepository, @@ -135,19 +132,3 @@ class JsonTokensRepository(AbstractTokensRepository): expires_at=tokens_file["new_device"]["expiration"], ) return new_device_key - - def use_mnemonic_new_device_key( - self, mnemonic_phrase: str, device_name: str - ) -> Token: - """Use the mnemonic new device key""" - new_device_key = self._get_stored_new_device_key() - if not new_device_key: - raise NewDeviceKeyNotFound - - if not self._assert_mnemonic(new_device_key.key, mnemonic_phrase): - raise NewDeviceKeyNotFound("Phrase is not token!") - - new_token = self.create_token(device_name=device_name) - self.delete_new_device_key() - - return new_token From f2fa47466bf3d8aab6f857cd4291fe2034750838 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 14:29:19 +0000 Subject: [PATCH 37/56] feat(tokens-repo):empty implementation of redis token repo But it initializes and fails tests! --- .../tokens/redis_tokens_repository.py | 54 ++++++++++++++++++- .../test_repository/test_tokens_repository.py | 6 +-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 0186c11..13d49c9 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -1,9 +1,18 @@ """ Token repository using Redis as backend. """ +from typing import Optional +from datetime import datetime + from selfprivacy_api.repositories.tokens.abstract_tokens_repository import ( AbstractTokensRepository, ) +from selfprivacy_api.utils.redis_pool import RedisPool +from selfprivacy_api.models.tokens.token import Token +from selfprivacy_api.models.tokens.recovery_key import RecoveryKey +from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey + +TOKENS_PREFIX = "token_repo:tokens:" class RedisTokensRepository(AbstractTokensRepository): @@ -11,5 +20,48 @@ class RedisTokensRepository(AbstractTokensRepository): Token repository using Redis as a backend """ - def __init__(self) -> None: + def __init__(self): + self.connection = RedisPool().get_connection() + + def token_key_for_device(device_name: str): + return TOKENS_PREFIX + str(hash(device_name)) + + def get_tokens(self) -> list[Token]: + """Get the tokens""" + raise NotImplementedError + + def delete_token(self, input_token: Token) -> None: + """Delete the token""" + raise NotImplementedError + + def get_recovery_key(self) -> Optional[RecoveryKey]: + """Get the recovery key""" + raise NotImplementedError + + def create_recovery_key( + self, + expiration: Optional[datetime], + uses_left: Optional[int], + ) -> RecoveryKey: + """Create the recovery key""" + raise NotImplementedError + + def get_new_device_key(self) -> NewDeviceKey: + """Creates and returns the new device key""" + raise NotImplementedError + + def delete_new_device_key(self) -> None: + """Delete the new device key""" + raise NotImplementedError + + def _store_token(self, new_token: Token): + """Store a token directly""" + raise NotImplementedError + + def _decrement_recovery_token(self): + """Decrement recovery key use count by one""" + raise NotImplementedError + + def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: + """Retrieves new device key that is already stored.""" raise NotImplementedError diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index bdad6d8..dff1799 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -166,12 +166,12 @@ def empty_redis_repo(): @pytest.fixture(params=["json", "redis"]) -def empty_repo(request, empty_json_repo): +def empty_repo(request, empty_json_repo, empty_redis_repo): if request.param == "json": return empty_json_repo if request.param == "redis": - # return empty_redis_repo - return empty_json_repo + return empty_redis_repo + # return empty_json_repo else: raise NotImplementedError From 256c16fa9fea641d96e0685d038fca7bba30b73a Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 14:48:43 +0000 Subject: [PATCH 38/56] feat(tokens-repo): redis get tokens --- .../tokens/redis_tokens_repository.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 13d49c9..184fa8f 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -28,7 +28,9 @@ class RedisTokensRepository(AbstractTokensRepository): def get_tokens(self) -> list[Token]: """Get the tokens""" - raise NotImplementedError + r = self.connection + token_keys = r.keys(TOKENS_PREFIX + "*") + return [self._token_from_hash(key) for key in token_keys] def delete_token(self, input_token: Token) -> None: """Delete the token""" @@ -65,3 +67,20 @@ class RedisTokensRepository(AbstractTokensRepository): def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: """Retrieves new device key that is already stored.""" raise NotImplementedError + + def _token_from_hash(self, redis_key: str) -> Token: + r = self.connection + if r.exists(redis_key): + token_dict = r.hgetall(redis_key) + for date in [ + "created_at", + ]: + if token_dict[date] != "None": + token_dict[date] = datetime.datetime.fromisoformat(token_dict[date]) + for key in token_dict.keys(): + if token_dict[key] == "None": + token_dict[key] = None + + return Token(**token_dict) + return None + From d8e3cd67e0517c717a4f0c8c264dc7b9fec3eb70 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 15:21:32 +0000 Subject: [PATCH 39/56] feat(tokens-repo): redis store token --- .../repositories/tokens/redis_tokens_repository.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 184fa8f..72a3cec 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -23,6 +23,7 @@ class RedisTokensRepository(AbstractTokensRepository): def __init__(self): self.connection = RedisPool().get_connection() + @staticmethod def token_key_for_device(device_name: str): return TOKENS_PREFIX + str(hash(device_name)) @@ -58,7 +59,8 @@ class RedisTokensRepository(AbstractTokensRepository): def _store_token(self, new_token: Token): """Store a token directly""" - raise NotImplementedError + key = RedisTokensRepository.token_key_for_device(new_token.device_name) + self._store_token_as_hash(key, new_token) def _decrement_recovery_token(self): """Decrement recovery key use count by one""" @@ -76,7 +78,7 @@ class RedisTokensRepository(AbstractTokensRepository): "created_at", ]: if token_dict[date] != "None": - token_dict[date] = datetime.datetime.fromisoformat(token_dict[date]) + token_dict[date] = datetime.fromisoformat(token_dict[date]) for key in token_dict.keys(): if token_dict[key] == "None": token_dict[key] = None @@ -84,3 +86,9 @@ class RedisTokensRepository(AbstractTokensRepository): return Token(**token_dict) return None + def _store_token_as_hash(self, redis_key, model): + r = self.connection + for key, value in model.dict().items(): + if isinstance(value, datetime): + value = value.isoformat() + r.hset(redis_key, key, str(value)) From ba6a5261fa39e428c76b73394d8a17f263ff8ffb Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 15:34:25 +0000 Subject: [PATCH 40/56] refactor(tokens-repo): redis token key func --- .../repositories/tokens/redis_tokens_repository.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 72a3cec..44c32f3 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -57,9 +57,13 @@ class RedisTokensRepository(AbstractTokensRepository): """Delete the new device key""" raise NotImplementedError + @staticmethod + def _token_redis_key(token: Token) -> str: + return RedisTokensRepository.token_key_for_device(token.device_name) + def _store_token(self, new_token: Token): """Store a token directly""" - key = RedisTokensRepository.token_key_for_device(new_token.device_name) + key = RedisTokensRepository._token_redis_key(new_token) self._store_token_as_hash(key, new_token) def _decrement_recovery_token(self): From 647e02f25b3fe84e1b3f4f48f91961ab0349bde4 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 16:16:55 +0000 Subject: [PATCH 41/56] feat(tokens-repo): redis delete token --- .../repositories/tokens/redis_tokens_repository.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 44c32f3..86d5e51 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -35,7 +35,9 @@ class RedisTokensRepository(AbstractTokensRepository): def delete_token(self, input_token: Token) -> None: """Delete the token""" - raise NotImplementedError + r = self.connection + key = RedisTokensRepository._token_redis_key(input_token) + r.delete(key) def get_recovery_key(self) -> Optional[RecoveryKey]: """Get the recovery key""" From e504585437868ba788cd87751494f090d24a79b5 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 16:29:38 +0000 Subject: [PATCH 42/56] test(tokens-repo): do not require order --- .../test_repository/test_tokens_repository.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index dff1799..0a0382b 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -181,8 +181,8 @@ def some_tokens_repo(empty_repo): for name in ORIGINAL_DEVICE_NAMES: empty_repo.create_token(name) assert len(empty_repo.get_tokens()) == len(ORIGINAL_DEVICE_NAMES) - for i, t in enumerate(empty_repo.get_tokens()): - assert t.device_name == ORIGINAL_DEVICE_NAMES[i] + for name in ORIGINAL_DEVICE_NAMES: + assert empty_repo.get_token_by_name(name) is not None assert empty_repo.get_new_device_key() is not None return empty_repo @@ -209,8 +209,10 @@ def test_get_token_by_non_existent_token_string(some_tokens_repo): def test_get_token_by_name(some_tokens_repo): repo = some_tokens_repo - assert repo.get_token_by_name(token_name="primary_token") is not None - assert repo.get_token_by_name(token_name="primary_token") == repo.get_tokens()[0] + token = repo.get_token_by_name(token_name="primary_token") + assert token is not None + assert token.device_name == "primary_token" + assert token in repo.get_tokens() def test_get_token_by_non_existent_name(some_tokens_repo): From 3cb7f295934f20e5bc7af1f0d40a4e185892159e Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:03:22 +0000 Subject: [PATCH 43/56] refactor(tokens-repo): detach preparing a dict before a model cast --- .../tokens/redis_tokens_repository.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 86d5e51..accaacc 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -76,18 +76,22 @@ class RedisTokensRepository(AbstractTokensRepository): """Retrieves new device key that is already stored.""" raise NotImplementedError + @staticmethod + def _prepare_model_dict(d: dict): + for date in [ + "created_at", + ]: + if d[date] != "None": + d[date] = datetime.fromisoformat(d[date]) + for key in d.keys(): + if d[key] == "None": + d[key] = None + def _token_from_hash(self, redis_key: str) -> Token: r = self.connection if r.exists(redis_key): token_dict = r.hgetall(redis_key) - for date in [ - "created_at", - ]: - if token_dict[date] != "None": - token_dict[date] = datetime.fromisoformat(token_dict[date]) - for key in token_dict.keys(): - if token_dict[key] == "None": - token_dict[key] = None + RedisTokensRepository._prepare_model_dict(token_dict) return Token(**token_dict) return None From b98ccb88d162276f5bdc11978c954a47887c5d66 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:10:32 +0000 Subject: [PATCH 44/56] refactor(tokens-repo): separate getting model dict --- .../repositories/tokens/redis_tokens_repository.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index accaacc..af45384 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -87,12 +87,17 @@ class RedisTokensRepository(AbstractTokensRepository): if d[key] == "None": d[key] = None - def _token_from_hash(self, redis_key: str) -> Token: + def _model_dict_from_hash(self, redis_key: str) -> Optional[dict]: r = self.connection if r.exists(redis_key): token_dict = r.hgetall(redis_key) RedisTokensRepository._prepare_model_dict(token_dict) + return token_dict + return None + def _token_from_hash(self, redis_key: str) -> Optional[Token]: + token_dict = self._model_dict_from_hash(redis_key) + if token_dict is not None: return Token(**token_dict) return None From 9ffd67fa1947f0ad2835b78adbf94240d84ec507 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:20:09 +0000 Subject: [PATCH 45/56] feat(tokens-repo): get new device key --- .../repositories/tokens/redis_tokens_repository.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index af45384..f99d215 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -13,6 +13,7 @@ from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey TOKENS_PREFIX = "token_repo:tokens:" +NEW_DEVICE_KEY_REDIS_KEY = "token_repo:new_device_key" class RedisTokensRepository(AbstractTokensRepository): @@ -53,7 +54,9 @@ class RedisTokensRepository(AbstractTokensRepository): def get_new_device_key(self) -> NewDeviceKey: """Creates and returns the new device key""" - raise NotImplementedError + new_device_key = NewDeviceKey.generate() + self._store_model_as_hash(NEW_DEVICE_KEY_REDIS_KEY, new_device_key) + return NewDeviceKey def delete_new_device_key(self) -> None: """Delete the new device key""" @@ -66,7 +69,7 @@ class RedisTokensRepository(AbstractTokensRepository): def _store_token(self, new_token: Token): """Store a token directly""" key = RedisTokensRepository._token_redis_key(new_token) - self._store_token_as_hash(key, new_token) + self._store_model_as_hash(key, new_token) def _decrement_recovery_token(self): """Decrement recovery key use count by one""" @@ -101,7 +104,7 @@ class RedisTokensRepository(AbstractTokensRepository): return Token(**token_dict) return None - def _store_token_as_hash(self, redis_key, model): + def _store_model_as_hash(self, redis_key, model): r = self.connection for key, value in model.dict().items(): if isinstance(value, datetime): From 95e200bfc5b7dc99ee22ca92154a294ef2dfe661 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:31:32 +0000 Subject: [PATCH 46/56] feat(tokens-repo): reset function --- .../repositories/tokens/redis_tokens_repository.py | 5 +++++ tests/test_graphql/test_repository/test_tokens_repository.py | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index f99d215..be3615f 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -11,6 +11,7 @@ from selfprivacy_api.utils.redis_pool import RedisPool from selfprivacy_api.models.tokens.token import Token from selfprivacy_api.models.tokens.recovery_key import RecoveryKey from selfprivacy_api.models.tokens.new_device_key import NewDeviceKey +from selfprivacy_api.repositories.tokens.exceptions import TokenNotFound TOKENS_PREFIX = "token_repo:tokens:" NEW_DEVICE_KEY_REDIS_KEY = "token_repo:new_device_key" @@ -40,6 +41,10 @@ class RedisTokensRepository(AbstractTokensRepository): key = RedisTokensRepository._token_redis_key(input_token) r.delete(key) + def reset(self): + for token in self.get_tokens(): + self.delete_token(token) + def get_recovery_key(self) -> Optional[RecoveryKey]: """Get the recovery key""" raise NotImplementedError diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 0a0382b..05ad77b 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -159,8 +159,7 @@ def empty_json_repo(empty_keys): @pytest.fixture def empty_redis_repo(): repo = RedisTokensRepository() - for token in repo.get_tokens(): - repo.delete_token(token) + repo.reset() assert repo.get_tokens() == [] return repo From bf6c230ae08c01aa97316ac31bce182143948edf Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:41:47 +0000 Subject: [PATCH 47/56] fix(tokens-repo): raise token not found when deleting nonexistent token even if device name exists --- selfprivacy_api/repositories/tokens/redis_tokens_repository.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index be3615f..8432709 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -39,6 +39,8 @@ class RedisTokensRepository(AbstractTokensRepository): """Delete the token""" r = self.connection key = RedisTokensRepository._token_redis_key(input_token) + if input_token not in self.get_tokens(): + raise TokenNotFound r.delete(key) def reset(self): From 257096084f872f160e327bf1d2513b6ce3762578 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 17:51:51 +0000 Subject: [PATCH 48/56] refactor(tokens-repo): split out date field detection --- .../repositories/tokens/redis_tokens_repository.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 8432709..3a23911 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -87,10 +87,15 @@ class RedisTokensRepository(AbstractTokensRepository): raise NotImplementedError @staticmethod - def _prepare_model_dict(d: dict): - for date in [ + def _is_date_key(key: str): + return key in [ "created_at", - ]: + ] + + @staticmethod + def _prepare_model_dict(d: dict): + date_keys = [key for key in d.keys() if RedisTokensRepository._is_date_key(key)] + for date in date_keys: if d[date] != "None": d[date] = datetime.fromisoformat(d[date]) for key in d.keys(): From 4579fec569ff6da3c2f1b789b47c76be8d46e290 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:01:34 +0000 Subject: [PATCH 49/56] feat(tokens-repo): get recovery key --- .../repositories/tokens/redis_tokens_repository.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 3a23911..ae91b32 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -15,6 +15,7 @@ from selfprivacy_api.repositories.tokens.exceptions import TokenNotFound TOKENS_PREFIX = "token_repo:tokens:" NEW_DEVICE_KEY_REDIS_KEY = "token_repo:new_device_key" +RECOVERY_KEY_REDIS_KEY = "token_repo:recovery_key" class RedisTokensRepository(AbstractTokensRepository): @@ -49,7 +50,10 @@ class RedisTokensRepository(AbstractTokensRepository): def get_recovery_key(self) -> Optional[RecoveryKey]: """Get the recovery key""" - raise NotImplementedError + r = self.connection + if r.exists(RECOVERY_KEY_REDIS_KEY): + return self._recovery_key_from_hash(RECOVERY_KEY_REDIS_KEY) + return None def create_recovery_key( self, @@ -90,6 +94,7 @@ class RedisTokensRepository(AbstractTokensRepository): def _is_date_key(key: str): return key in [ "created_at", + "expires_at", ] @staticmethod @@ -116,6 +121,12 @@ class RedisTokensRepository(AbstractTokensRepository): return Token(**token_dict) return None + def _recovery_key_from_hash(self, redis_key: str) -> Optional[RecoveryKey]: + token_dict = self._model_dict_from_hash(redis_key) + if token_dict is not None: + return RecoveryKey(**token_dict) + return None + def _store_model_as_hash(self, redis_key, model): r = self.connection for key, value in model.dict().items(): From 8dfb3eb9369d266f2e35e10f7597a8c182de557b Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:06:11 +0000 Subject: [PATCH 50/56] feat(tokens-repo): fuller reset --- .../repositories/tokens/redis_tokens_repository.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index ae91b32..ad9d26b 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -47,6 +47,9 @@ class RedisTokensRepository(AbstractTokensRepository): def reset(self): for token in self.get_tokens(): self.delete_token(token) + self.delete_new_device_key() + r = self.connection + r.delete(RECOVERY_KEY_REDIS_KEY) def get_recovery_key(self) -> Optional[RecoveryKey]: """Get the recovery key""" @@ -71,7 +74,8 @@ class RedisTokensRepository(AbstractTokensRepository): def delete_new_device_key(self) -> None: """Delete the new device key""" - raise NotImplementedError + r = self.connection + r.delete(NEW_DEVICE_KEY_REDIS_KEY) @staticmethod def _token_redis_key(token: Token) -> str: From eba1d01b3d648d23ecced682b8b0d613ad77f911 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:10:48 +0000 Subject: [PATCH 51/56] feat(tokens-repo): recovery key creation --- .../repositories/tokens/redis_tokens_repository.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index ad9d26b..4c3c46f 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -64,7 +64,9 @@ class RedisTokensRepository(AbstractTokensRepository): uses_left: Optional[int], ) -> RecoveryKey: """Create the recovery key""" - raise NotImplementedError + recovery_key = RecoveryKey.generate(expiration=expiration, uses_left=uses_left) + self._store_model_as_hash(RECOVERY_KEY_REDIS_KEY, recovery_key) + return recovery_key def get_new_device_key(self) -> NewDeviceKey: """Creates and returns the new device key""" From 13e84e2697ca38b5846e77328086d059248d85c5 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:22:19 +0000 Subject: [PATCH 52/56] feat(tokens-repo): recovery key uses decrement --- .../repositories/tokens/redis_tokens_repository.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 4c3c46f..0804898 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -90,7 +90,10 @@ class RedisTokensRepository(AbstractTokensRepository): def _decrement_recovery_token(self): """Decrement recovery key use count by one""" - raise NotImplementedError + if self.is_recovery_key_valid(): + uses_left = self.get_recovery_key().uses_left + r = self.connection + r.hset(RECOVERY_KEY_REDIS_KEY, "uses_left", uses_left - 1) def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: """Retrieves new device key that is already stored.""" From fda5d315a961afb0b9b20fad26cd83cad6b3033e Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:27:58 +0000 Subject: [PATCH 53/56] fix(tokens-repo): return device key instead of NewDeviceKey class --- selfprivacy_api/repositories/tokens/redis_tokens_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 0804898..930b043 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -72,7 +72,7 @@ class RedisTokensRepository(AbstractTokensRepository): """Creates and returns the new device key""" new_device_key = NewDeviceKey.generate() self._store_model_as_hash(NEW_DEVICE_KEY_REDIS_KEY, new_device_key) - return NewDeviceKey + return new_device_key def delete_new_device_key(self) -> None: """Delete the new device key""" From 6f6a9f5ef070befa4b9e0ca9fd80713c6de59567 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:34:12 +0000 Subject: [PATCH 54/56] test(tokens-repo): do not require order in test_delete_not_found_token --- .../test_graphql/test_repository/test_tokens_repository.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_graphql/test_repository/test_tokens_repository.py b/tests/test_graphql/test_repository/test_tokens_repository.py index 05ad77b..43f7626 100644 --- a/tests/test_graphql/test_repository/test_tokens_repository.py +++ b/tests/test_graphql/test_repository/test_tokens_repository.py @@ -265,7 +265,7 @@ def test_delete_token(some_tokens_repo): def test_delete_not_found_token(some_tokens_repo): repo = some_tokens_repo - tokens = repo.get_tokens() + initial_tokens = repo.get_tokens() input_token = Token( token="imbadtoken", device_name="primary_token", @@ -274,7 +274,10 @@ def test_delete_not_found_token(some_tokens_repo): with pytest.raises(TokenNotFound): assert repo.delete_token(input_token) is None - assert repo.get_tokens() == tokens + new_tokens = repo.get_tokens() + assert len(new_tokens) == len(initial_tokens) + for token in initial_tokens: + assert token in new_tokens def test_refresh_token(some_tokens_repo, mock_token_generate): From 0ae7c43ebf2b11c6b7edb8ab9086d80f3cd88cdc Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:45:12 +0000 Subject: [PATCH 55/56] refactor(tokens-repo): break out generic hash_as_model casting --- .../repositories/tokens/redis_tokens_repository.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 930b043..833679f 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -124,17 +124,17 @@ class RedisTokensRepository(AbstractTokensRepository): return token_dict return None - def _token_from_hash(self, redis_key: str) -> Optional[Token]: + def _hash_as_model(self, redis_key: str, model_class): token_dict = self._model_dict_from_hash(redis_key) if token_dict is not None: - return Token(**token_dict) + return model_class(**token_dict) return None + def _token_from_hash(self, redis_key: str) -> Optional[Token]: + return self._hash_as_model(redis_key, Token) + def _recovery_key_from_hash(self, redis_key: str) -> Optional[RecoveryKey]: - token_dict = self._model_dict_from_hash(redis_key) - if token_dict is not None: - return RecoveryKey(**token_dict) - return None + return self._hash_as_model(redis_key, RecoveryKey) def _store_model_as_hash(self, redis_key, model): r = self.connection From 5a25e2a2706ef2492e225ec1d0777c9944dd0152 Mon Sep 17 00:00:00 2001 From: Houkime <> Date: Wed, 14 Dec 2022 18:55:26 +0000 Subject: [PATCH 56/56] feat(tokens-repo): getting stored device key --- .../repositories/tokens/redis_tokens_repository.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py index 833679f..b1fb4b0 100644 --- a/selfprivacy_api/repositories/tokens/redis_tokens_repository.py +++ b/selfprivacy_api/repositories/tokens/redis_tokens_repository.py @@ -97,7 +97,7 @@ class RedisTokensRepository(AbstractTokensRepository): def _get_stored_new_device_key(self) -> Optional[NewDeviceKey]: """Retrieves new device key that is already stored.""" - raise NotImplementedError + return self._new_device_key_from_hash(NEW_DEVICE_KEY_REDIS_KEY) @staticmethod def _is_date_key(key: str): @@ -136,6 +136,9 @@ class RedisTokensRepository(AbstractTokensRepository): def _recovery_key_from_hash(self, redis_key: str) -> Optional[RecoveryKey]: return self._hash_as_model(redis_key, RecoveryKey) + def _new_device_key_from_hash(self, redis_key: str) -> Optional[NewDeviceKey]: + return self._hash_as_model(redis_key, NewDeviceKey) + def _store_model_as_hash(self, redis_key, model): r = self.connection for key, value in model.dict().items():