From 8d29bcd951ce4d5348880888acf5cea3b7e4f560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Srokosz?= Date: Thu, 12 Dec 2024 12:14:12 +0100 Subject: [PATCH] v3: Drop support for legacy_api_key_v1 (#999) --- mwdb/app.py | 6 ---- mwdb/core/auth.py | 6 ++-- mwdb/core/deprecated.py | 2 -- mwdb/model/api_key.py | 9 ++++-- ...974831e_removed_user_version_uid_column.py | 32 +++++++++++++++++++ mwdb/model/user.py | 20 +----------- tests/backend/test_auth.py | 6 ---- 7 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 mwdb/model/migrations/versions/56adf974831e_removed_user_version_uid_column.py diff --git a/mwdb/app.py b/mwdb/app.py index 21b26136e..570609574 100755 --- a/mwdb/app.py +++ b/mwdb/app.py @@ -8,7 +8,6 @@ from mwdb.core.app import api, app from mwdb.core.config import app_config -from mwdb.core.deprecated import DeprecatedFeature, uses_deprecated_api from mwdb.core.log import getLogger, setup_logger from mwdb.core.metrics import metric_api_requests, metrics_enabled from mwdb.core.plugins import PluginAppContext, load_plugins @@ -204,11 +203,6 @@ def require_auth(): # Not a session token? Maybe APIKey token if g.auth_user is None: g.auth_user = APIKey.verify_token(token) - # Still nothing? Maybe legacy API key - if g.auth_user is None: - g.auth_user = User.verify_legacy_token(token) - if g.auth_user is not None: - uses_deprecated_api(DeprecatedFeature.legacy_api_key_v1) if g.auth_user: if ( diff --git a/mwdb/core/auth.py b/mwdb/core/auth.py index ab3f89ac2..1588e1647 100644 --- a/mwdb/core/auth.py +++ b/mwdb/core/auth.py @@ -1,6 +1,6 @@ import datetime from enum import Enum -from typing import Any, Set +from typing import Any import jwt @@ -52,14 +52,14 @@ def verify_token(token: str, scope: AuthScope) -> Any: return data -def verify_legacy_token(token: str, required_fields: Set[str]) -> Any: +def verify_legacy_api_key(token: str) -> Any: try: data = jwt.decode( token, key=app_config.mwdb.secret_key, algorithms=["HS512"], ) - if set(data.keys()) != required_fields: + if set(data.keys()) != {"login", "api_key_id"}: return None except jwt.InvalidTokenError: diff --git a/mwdb/core/deprecated.py b/mwdb/core/deprecated.py index df138a5fb..ea6259333 100644 --- a/mwdb/core/deprecated.py +++ b/mwdb/core/deprecated.py @@ -14,8 +14,6 @@ class DeprecatedFeature(Enum): - # Unmanageable API keys, deprecated in v2.0.0 - legacy_api_key_v1 = "legacy_api_key_v1" # API keys non-complaint with RFC7519 # Deprecated in v2.7.0 legacy_api_key_v2 = "legacy_api_key_v2" diff --git a/mwdb/model/api_key.py b/mwdb/model/api_key.py index 3b08bc11f..c1004e9cc 100644 --- a/mwdb/model/api_key.py +++ b/mwdb/model/api_key.py @@ -4,7 +4,12 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm.exc import NoResultFound -from mwdb.core.auth import AuthScope, generate_token, verify_legacy_token, verify_token +from mwdb.core.auth import ( + AuthScope, + generate_token, + verify_legacy_api_key, + verify_token, +) from mwdb.core.deprecated import DeprecatedFeature, uses_deprecated_api from . import db @@ -35,7 +40,7 @@ def verify_token(token): if data is None: # check for legacy API Token - data = verify_legacy_token(token, required_fields={"login", "api_key_id"}) + data = verify_legacy_api_key(token) if data is None: return None else: diff --git a/mwdb/model/migrations/versions/56adf974831e_removed_user_version_uid_column.py b/mwdb/model/migrations/versions/56adf974831e_removed_user_version_uid_column.py new file mode 100644 index 000000000..d6ad29010 --- /dev/null +++ b/mwdb/model/migrations/versions/56adf974831e_removed_user_version_uid_column.py @@ -0,0 +1,32 @@ +"""Removed User.version_uid column + +Revision ID: 56adf974831e +Revises: 52bb55f76ef3 +Create Date: 2024-12-11 18:21:42.442984 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "56adf974831e" +down_revision = "52bb55f76ef3" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("user", "version_uid") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "user", + sa.Column( + "version_uid", sa.VARCHAR(length=16), autoincrement=False, nullable=True + ), + ) + # ### end Alembic commands ### diff --git a/mwdb/model/user.py b/mwdb/model/user.py index 9c697d062..d0653c428 100644 --- a/mwdb/model/user.py +++ b/mwdb/model/user.py @@ -8,7 +8,7 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm.exc import NoResultFound -from mwdb.core.auth import AuthScope, generate_token, verify_legacy_token, verify_token +from mwdb.core.auth import AuthScope, generate_token, verify_token from mwdb.core.capabilities import Capabilities from . import db @@ -25,8 +25,6 @@ class User(db.Model): email = db.Column(db.String(128), nullable=False) password_hash = db.Column(db.String(128)) - # Legacy "version_uid", todo: remove it when users are ready - version_uid = db.Column(db.String(16)) # Password version (set password link and session token validation) # Invalidates set password link or session when password has been changes password_ver = db.Column(db.String(16)) @@ -224,22 +222,6 @@ def verify_set_password_token(token) -> Optional["User"]: ) return None if result is None else result[0] - @staticmethod - def verify_legacy_token(token): - data = verify_legacy_token(token, required_fields={"login", "version_uid"}) - if data is None: - return None - - try: - user_obj = User.query.filter(User.login == data["login"]).one() - except NoResultFound: - return None - - if user_obj.version_uid != data["version_uid"]: - return None - - return user_obj - def is_member(self, group_id): groups = db.session.query(Member.group_id).filter(Member.user_id == self.id) return group_id.in_(groups) diff --git a/tests/backend/test_auth.py b/tests/backend/test_auth.py index 6375fb42b..4a4a8ddc6 100644 --- a/tests/backend/test_auth.py +++ b/tests/backend/test_auth.py @@ -120,20 +120,14 @@ def test_jwt_legacy_api_keys(admin_session): secret_key = "e2e-testing-key" api_key_id = admin_session.api_key_create("admin", "testing-key").json()["id"] - pre2_0_payload = {"login": "admin", "version_uid": None} pre2_7_payload = {"login": "admin", "api_key_id": api_key_id} - pre2_0_token = jwt.encode(pre2_0_payload, secret_key, algorithm="HS512") pre2_7_token = jwt.encode(pre2_7_payload, secret_key, algorithm="HS512") session = MwdbTest() response = session.request("get", "/server") assert not response["is_authenticated"] - session.set_auth_token(pre2_0_token) - response = session.request("get", "/server") - assert response["is_authenticated"] - session.set_auth_token(pre2_7_token) response = session.request("get", "/server") assert response["is_authenticated"]