diff options
Diffstat (limited to 'server/szurubooru/tests')
| -rw-r--r-- | server/szurubooru/tests/api/test_user_token_creating.py | 29 | ||||
| -rw-r--r-- | server/szurubooru/tests/api/test_user_token_deleting.py | 30 | ||||
| -rw-r--r-- | server/szurubooru/tests/api/test_user_token_retrieving.py | 31 | ||||
| -rw-r--r-- | server/szurubooru/tests/api/test_user_token_updating.py | 42 | ||||
| -rw-r--r-- | server/szurubooru/tests/conftest.py | 25 | ||||
| -rw-r--r-- | server/szurubooru/tests/func/test_auth.py | 24 | ||||
| -rw-r--r-- | server/szurubooru/tests/func/test_user_tokens.py | 155 | ||||
| -rw-r--r-- | server/szurubooru/tests/middleware/__init__.py | 0 | ||||
| -rw-r--r-- | server/szurubooru/tests/middleware/test_authenticator.py | 93 | ||||
| -rw-r--r-- | server/szurubooru/tests/model/test_user_token.py | 14 |
10 files changed, 440 insertions, 3 deletions
diff --git a/server/szurubooru/tests/api/test_user_token_creating.py b/server/szurubooru/tests/api/test_user_token_creating.py new file mode 100644 index 0000000..f550f63 --- /dev/null +++ b/server/szurubooru/tests/api/test_user_token_creating.py @@ -0,0 +1,29 @@ +from unittest.mock import patch +import pytest +from szurubooru import api +from szurubooru.func import user_tokens, users + + +@pytest.fixture(autouse=True) +def inject_config(config_injector): + config_injector({'privileges': {'user_tokens:create:self': 'regular'}}) + + +def test_creating_user_token( + user_token_factory, context_factory, fake_datetime): + user_token = user_token_factory() + with patch('szurubooru.func.user_tokens.create_user_token'), \ + patch('szurubooru.func.user_tokens.serialize_user_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + fake_datetime('1969-02-12'): + users.get_user_by_name.return_value = user_token.user + user_tokens.serialize_user_token.return_value = 'serialized user token' + user_tokens.create_user_token.return_value = user_token + result = api.user_token_api.create_user_token( + context_factory(user=user_token.user), + { + 'user_name': user_token.user.name + }) + assert result == 'serialized user token' + user_tokens.create_user_token.assert_called_once_with( + user_token.user, True) diff --git a/server/szurubooru/tests/api/test_user_token_deleting.py b/server/szurubooru/tests/api/test_user_token_deleting.py new file mode 100644 index 0000000..8534152 --- /dev/null +++ b/server/szurubooru/tests/api/test_user_token_deleting.py @@ -0,0 +1,30 @@ +from unittest.mock import patch +import pytest +from szurubooru import api, db +from szurubooru.func import user_tokens, users + + +@pytest.fixture(autouse=True) +def inject_config(config_injector): + config_injector({'privileges': {'user_tokens:delete:self': 'regular'}}) + + +def test_deleting_user_token( + user_token_factory, context_factory, fake_datetime): + user_token = user_token_factory() + db.session.add(user_token) + db.session.commit() + with patch('szurubooru.func.user_tokens.get_by_user_and_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + fake_datetime('1969-02-12'): + users.get_user_by_name.return_value = user_token.user + user_tokens.get_by_user_and_token.return_value = user_token + result = api.user_token_api.delete_user_token( + context_factory(user=user_token.user), + { + 'user_name': user_token.user.name, + 'user_token': user_token.token + }) + assert result == {} + user_tokens.get_by_user_and_token.assert_called_once_with( + user_token.user, user_token.token) diff --git a/server/szurubooru/tests/api/test_user_token_retrieving.py b/server/szurubooru/tests/api/test_user_token_retrieving.py new file mode 100644 index 0000000..01b2534 --- /dev/null +++ b/server/szurubooru/tests/api/test_user_token_retrieving.py @@ -0,0 +1,31 @@ +from unittest.mock import patch +import pytest +from szurubooru import api +from szurubooru.func import user_tokens, users + + +@pytest.fixture(autouse=True) +def inject_config(config_injector): + config_injector({'privileges': {'user_tokens:list:self': 'regular'}}) + + +def test_retrieving_user_tokens( + user_token_factory, context_factory, fake_datetime): + user_token1 = user_token_factory() + user_token2 = user_token_factory(user=user_token1.user) + user_token3 = user_token_factory(user=user_token1.user) + with patch('szurubooru.func.user_tokens.get_user_tokens'), \ + patch('szurubooru.func.user_tokens.serialize_user_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + fake_datetime('1969-02-12'): + users.get_user_by_name.return_value = user_token1.user + user_tokens.serialize_user_token.return_value = 'serialized user token' + user_tokens.get_user_tokens.return_value = [user_token1, user_token2, + user_token3] + result = api.user_token_api.get_user_tokens( + context_factory(user=user_token1.user), + { + 'user_name': user_token1.user.name + }) + assert result == {'results': ['serialized user token'] * 3} + user_tokens.get_user_tokens.assert_called_once_with(user_token1.user) diff --git a/server/szurubooru/tests/api/test_user_token_updating.py b/server/szurubooru/tests/api/test_user_token_updating.py new file mode 100644 index 0000000..bf725a3 --- /dev/null +++ b/server/szurubooru/tests/api/test_user_token_updating.py @@ -0,0 +1,42 @@ +from unittest.mock import patch +import pytest +from szurubooru import api, db +from szurubooru.func import user_tokens, users + + +@pytest.fixture(autouse=True) +def inject_config(config_injector): + config_injector({'privileges': {'user_tokens:edit:self': 'regular'}}) + + +def test_edit_user_token(user_token_factory, context_factory, fake_datetime): + user_token = user_token_factory() + db.session.add(user_token) + db.session.commit() + with patch('szurubooru.func.user_tokens.get_by_user_and_token'), \ + patch('szurubooru.func.user_tokens.update_user_token_enabled'), \ + patch('szurubooru.func.user_tokens.update_user_token_edit_time'), \ + patch('szurubooru.func.user_tokens.serialize_user_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + fake_datetime('1969-02-12'): + users.get_user_by_name.return_value = user_token.user + user_tokens.serialize_user_token.return_value = 'serialized user token' + user_tokens.get_by_user_and_token.return_value = user_token + result = api.user_token_api.update_user_token( + context_factory( + params={ + 'version': user_token.version, + 'enabled': False, + }, + user=user_token.user), + { + 'user_name': user_token.user.name, + 'user_token': user_token.token + }) + assert result == 'serialized user token' + user_tokens.get_by_user_and_token.assert_called_once_with( + user_token.user, user_token.token) + user_tokens.update_user_token_enabled.assert_called_once_with( + user_token, False) + user_tokens.update_user_token_edit_time.assert_called_once_with( + user_token) diff --git a/server/szurubooru/tests/conftest.py b/server/szurubooru/tests/conftest.py index db7806e..f6eeee1 100644 --- a/server/szurubooru/tests/conftest.py +++ b/server/szurubooru/tests/conftest.py @@ -93,11 +93,11 @@ def session(query_logger): # pylint: disable=unused-argument @pytest.fixture def context_factory(session): - def factory(params=None, files=None, user=None): + def factory(params=None, files=None, user=None, headers=None): ctx = rest.Context( method=None, url=None, - headers={}, + headers=headers or {}, params=params or {}, files=files or {}) ctx.session = session @@ -134,6 +134,27 @@ def user_factory(): @pytest.fixture +def user_token_factory(user_factory): + def factory( + user=None, + token=None, + expiration_time=None, + enabled=None, + creation_time=None): + if user is None: + user = user_factory() + db.session.add(user) + user_token = model.UserToken() + user_token.user = user + user_token.token = token or 'dummy' + user_token.expiration_time = expiration_time + user_token.enabled = enabled if enabled is not None else True + user_token.creation_time = creation_time or datetime(1997, 1, 1) + return user_token + return factory + + +@pytest.fixture def tag_category_factory(): def factory(name=None, color='dummy', default=False): category = model.TagCategory() diff --git a/server/szurubooru/tests/func/test_auth.py b/server/szurubooru/tests/func/test_auth.py index 5d0955a..6dc79bb 100644 --- a/server/szurubooru/tests/func/test_auth.py +++ b/server/szurubooru/tests/func/test_auth.py @@ -1,5 +1,6 @@ -from szurubooru.func import auth +from datetime import datetime, timedelta import pytest +from szurubooru.func import auth @pytest.fixture(autouse=True) @@ -41,3 +42,24 @@ def test_is_valid_password_auto_upgrades_user_password_hash(user_factory): assert result is True assert user.password_hash != hash assert user.password_revision > revision + + +def test_is_valid_token(user_token_factory): + user_token = user_token_factory() + assert auth.is_valid_token(user_token) + + +def test_expired_token_is_invalid(user_token_factory): + past_expiration = (datetime.utcnow() - timedelta(minutes=30)) + user_token = user_token_factory(expiration_time=past_expiration) + assert not auth.is_valid_token(user_token) + + +def test_disabled_token_is_invalid(user_token_factory): + user_token = user_token_factory(enabled=False) + assert not auth.is_valid_token(user_token) + + +def test_generate_authorization_token(): + result = auth.generate_authorization_token() + assert result != auth.generate_authorization_token() diff --git a/server/szurubooru/tests/func/test_user_tokens.py b/server/szurubooru/tests/func/test_user_tokens.py new file mode 100644 index 0000000..8c3577c --- /dev/null +++ b/server/szurubooru/tests/func/test_user_tokens.py @@ -0,0 +1,155 @@ +from datetime import datetime, timedelta +from unittest.mock import patch +import pytest +import pytz +import random +import string +from szurubooru import db, model +from szurubooru.func import user_tokens, users, auth, util + + +def test_serialize_user_token(user_token_factory): + user_token = user_token_factory() + db.session.add(user_token) + db.session.flush() + with patch('szurubooru.func.users.get_avatar_url'): + users.get_avatar_url.return_value = 'https://example.com/avatar.png' + result = user_tokens.serialize_user_token(user_token, user_token.user) + assert result == { + 'creationTime': datetime(1997, 1, 1, 0, 0), + 'enabled': True, + 'expirationTime': None, + 'lastEditTime': None, + 'lastUsageTime': None, + 'note': None, + 'token': 'dummy', + 'user': { + 'avatarUrl': 'https://example.com/avatar.png', + 'name': user_token.user.name}, + 'version': 1 + } + + +def test_serialize_user_token_none(): + result = user_tokens.serialize_user_token(None, None) + assert result is None + + +def test_get_by_user_and_token(user_token_factory): + user_token = user_token_factory() + db.session.add(user_token) + db.session.flush() + db.session.commit() + result = user_tokens.get_by_user_and_token( + user_token.user, user_token.token) + assert result == user_token + + +def test_get_user_tokens(user_token_factory): + user_token1 = user_token_factory() + user_token2 = user_token_factory(user=user_token1.user) + db.session.add(user_token1) + db.session.add(user_token2) + db.session.flush() + db.session.commit() + result = user_tokens.get_user_tokens(user_token1.user) + assert result == [user_token1, user_token2] + + +def test_create_user_token(user_factory): + user = user_factory() + db.session.add(user) + db.session.flush() + db.session.commit() + with patch('szurubooru.func.auth.generate_authorization_token'): + auth.generate_authorization_token.return_value = 'test' + result = user_tokens.create_user_token(user, True) + assert result.token == 'test' + assert result.user == user + + +def test_update_user_token_enabled(user_token_factory): + user_token = user_token_factory() + user_tokens.update_user_token_enabled(user_token, False) + assert user_token.enabled is False + assert user_token.last_edit_time is not None + + +def test_update_user_token_edit_time(user_token_factory): + user_token = user_token_factory() + assert user_token.last_edit_time is None + user_tokens.update_user_token_edit_time(user_token) + assert user_token.last_edit_time is not None + + +def test_update_user_token_note(user_token_factory): + user_token = user_token_factory() + assert user_token.note is None + user_tokens.update_user_token_note(user_token, ' Test Note ') + assert user_token.note == 'Test Note' + assert user_token.last_edit_time is not None + + +def test_update_user_token_note_input_too_long(user_token_factory): + user_token = user_token_factory() + assert user_token.note is None + note_max_length = util.get_column_size(model.UserToken.note) + 1 + note = ''.join( + random.choice(string.ascii_letters) for _ in range(note_max_length)) + with pytest.raises(user_tokens.InvalidNoteError): + user_tokens.update_user_token_note(user_token, note) + + +def test_update_user_token_expiration_time(user_token_factory): + user_token = user_token_factory() + assert user_token.expiration_time is None + expiration_time_str = ( + (datetime.utcnow() + timedelta(days=1)) + .replace(tzinfo=pytz.utc) + ).isoformat() + user_tokens.update_user_token_expiration_time( + user_token, expiration_time_str) + assert user_token.expiration_time.isoformat() == expiration_time_str + assert user_token.last_edit_time is not None + + +def test_update_user_token_expiration_time_in_past(user_token_factory): + user_token = user_token_factory() + assert user_token.expiration_time is None + expiration_time_str = ( + (datetime.utcnow() - timedelta(days=1)) + .replace(tzinfo=pytz.utc) + ).isoformat() + with pytest.raises( + user_tokens.InvalidExpirationError, + match='Expiration cannot happen in the past'): + user_tokens.update_user_token_expiration_time( + user_token, expiration_time_str) + + +@pytest.mark.parametrize('expiration_time_str', [ + datetime.utcnow().isoformat(), + (datetime.utcnow() - timedelta(days=1)).ctime(), + '1970/01/01 00:00:01.0000Z', + '70/01/01 00:00:01.0000Z', + ''.join(random.choice(string.ascii_letters) for _ in range(15)), + ''.join(random.choice(string.digits) for _ in range(8)) +]) +def test_update_user_token_expiration_time_invalid_format( + expiration_time_str, user_token_factory): + user_token = user_token_factory() + assert user_token.expiration_time is None + + with pytest.raises( + user_tokens.InvalidExpirationError, + match='Expiration is in an invalid format %s' + % expiration_time_str): + user_tokens.update_user_token_expiration_time( + user_token, expiration_time_str) + + +def test_bump_usage_time(user_token_factory, fake_datetime): + user_token = user_token_factory() + with fake_datetime('1997-01-01'): + user_tokens.bump_usage_time(user_token) + assert user_token.last_usage_time == datetime(1997, 1, 1) diff --git a/server/szurubooru/tests/middleware/__init__.py b/server/szurubooru/tests/middleware/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/server/szurubooru/tests/middleware/__init__.py diff --git a/server/szurubooru/tests/middleware/test_authenticator.py b/server/szurubooru/tests/middleware/test_authenticator.py new file mode 100644 index 0000000..be21a93 --- /dev/null +++ b/server/szurubooru/tests/middleware/test_authenticator.py @@ -0,0 +1,93 @@ +from unittest.mock import patch +import pytest +from szurubooru import db +from szurubooru.func import auth, users, user_tokens +from szurubooru.middleware import authenticator +from szurubooru.rest import errors + + +def test_process_request_no_header(context_factory): + ctx = context_factory() + authenticator.process_request(ctx) + assert ctx.user.name is None + + +def test_process_request_bump_login(context_factory, user_factory): + user = user_factory() + db.session.add(user) + db.session.flush() + ctx = context_factory( + headers={ + 'Authorization': 'Basic dGVzdFVzZXI6dGVzdFRva2Vu' + }, + params={ + 'bump-login': 'true' + }) + with patch('szurubooru.func.auth.is_valid_password'), \ + patch('szurubooru.func.users.get_user_by_name'): + users.get_user_by_name.return_value = user + auth.is_valid_password.return_value = True + authenticator.process_request(ctx) + assert user.last_login_time is not None + + +def test_process_request_bump_login_with_token( + context_factory, user_token_factory): + user_token = user_token_factory() + db.session.add(user_token) + db.session.flush() + ctx = context_factory( + headers={ + 'Authorization': 'Token dGVzdFVzZXI6dGVzdFRva2Vu' + }, + params={ + 'bump-login': 'true' + }) + with patch('szurubooru.func.auth.is_valid_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + patch('szurubooru.func.user_tokens.get_by_user_and_token'): + users.get_user_by_name.return_value = user_token.user + user_tokens.get_by_user_and_token.return_value = user_token + auth.is_valid_token.return_value = True + authenticator.process_request(ctx) + assert user_token.user.last_login_time is not None + assert user_token.last_usage_time is not None + + +def test_process_request_basic_auth_valid(context_factory, user_factory): + user = user_factory() + ctx = context_factory( + headers={ + 'Authorization': 'Basic dGVzdFVzZXI6dGVzdFBhc3N3b3Jk' + }) + with patch('szurubooru.func.auth.is_valid_password'), \ + patch('szurubooru.func.users.get_user_by_name'): + users.get_user_by_name.return_value = user + auth.is_valid_password.return_value = True + authenticator.process_request(ctx) + assert ctx.user == user + + +def test_process_request_token_auth_valid(context_factory, user_token_factory): + user_token = user_token_factory() + ctx = context_factory( + headers={ + 'Authorization': 'Token dGVzdFVzZXI6dGVzdFRva2Vu' + }) + with patch('szurubooru.func.auth.is_valid_token'), \ + patch('szurubooru.func.users.get_user_by_name'), \ + patch('szurubooru.func.user_tokens.get_by_user_and_token'): + users.get_user_by_name.return_value = user_token.user + user_tokens.get_by_user_and_token.return_value = user_token + auth.is_valid_token.return_value = True + authenticator.process_request(ctx) + assert ctx.user == user_token.user + + +def test_process_request_bad_header(context_factory): + ctx = context_factory( + headers={ + 'Authorization': 'Secret SuperSecretValue' + }) + with pytest.raises(errors.HttpBadRequest): + authenticator.process_request(ctx) diff --git a/server/szurubooru/tests/model/test_user_token.py b/server/szurubooru/tests/model/test_user_token.py new file mode 100644 index 0000000..0280082 --- /dev/null +++ b/server/szurubooru/tests/model/test_user_token.py @@ -0,0 +1,14 @@ +from datetime import datetime +from szurubooru import db + + +def test_saving_user_token(user_token_factory): + user_token = user_token_factory() + db.session.add(user_token) + db.session.flush() + db.session.refresh(user_token) + assert not db.session.dirty + assert user_token.user is not None + assert user_token.token == 'dummy' + assert user_token.enabled is True + assert user_token.creation_time == datetime(1997, 1, 1) |