From 155256359ea2e6b7f384e753e28d460e79d959d2 Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Fri, 18 Oct 2024 17:29:59 -0400 Subject: [PATCH 1/5] Add self delete option --- auth_backend/routes/user.py | 49 ++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/auth_backend/routes/user.py b/auth_backend/routes/user.py index c9227479..74527aa0 100644 --- a/auth_backend/routes/user.py +++ b/auth_backend/routes/user.py @@ -1,9 +1,10 @@ import logging from typing import Any, Literal -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, HTTPException, Query from fastapi_sqlalchemy import db from sqlalchemy.orm import Session +from starlette.status import HTTP_403_FORBIDDEN from auth_backend.auth_method import AuthPluginMeta from auth_backend.auth_plugins.email import Email @@ -141,26 +142,34 @@ async def patch_user( @user.delete("/{user_id}", response_model=None) async def delete_user( user_id: int, - current_user: UserSession = Depends(UnionAuth(scopes=["auth.user.delete"], allow_none=False, auto_error=True)), + current_user: UserSession = Depends(UnionAuth(scopes=[], allow_none=False, auto_error=True)), ) -> None: """ - Scopes: `["auth.user.delete"]` + Scopes: `["auth.user.delete"]` or `["auth.self.delete"]` for self delete """ - logger.debug(f'User id={current_user.id} triggered delete_user') - old_user = {"user_id": current_user.id} - user: User = User.get(user_id, session=db.session) + session_scopes = set([scope.name.lower() for scope in current_user.scopes]) + if ( + "auth.user.delete" in session_scopes + or "auth.self.delete" in session_scopes + and user_id == current_user.user_id + ): + logger.debug(f'User id={current_user.id} triggered delete_user') + old_user = {"user_id": current_user.id} + user: User = User.get(user_id, session=db.session) - for method in user._auth_methods: - if method.is_deleted: - continue - # Сохраняем старое состояние пользователя - if method.auth_method not in old_user: - old_user[method.auth_method] = {} - old_user[method.auth_method][method.param] = method.value - # Удаляем AuthMethod - AuthMethod.delete(method.id, session=db.session) - logger.info(f'{method=} for {user.id=} deleted') - User.delete(user_id, session=db.session) - db.session.commit() - await AuthPluginMeta.user_updated(None, old_user) - logger.info(f'{user=} deleted') + for method in user._auth_methods: + if method.is_deleted: + continue + # Сохраняем старое состояние пользователя + if method.auth_method not in old_user: + old_user[method.auth_method] = {} + old_user[method.auth_method][method.param] = method.value + # Удаляем AuthMethod + AuthMethod.delete(method.id, session=db.session) + logger.info(f'{method=} for {user.id=} deleted') + User.delete(user_id, session=db.session) + db.session.commit() + await AuthPluginMeta.user_updated(None, old_user) + logger.info(f'{user=} deleted') + else: + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Not authorized") From 11ae80a70fa9ce6d0511d969e3733d8b0ad80077 Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Fri, 18 Oct 2024 17:30:33 -0400 Subject: [PATCH 2/5] Style fix --- auth_backend/routes/user.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/auth_backend/routes/user.py b/auth_backend/routes/user.py index 74527aa0..44a28832 100644 --- a/auth_backend/routes/user.py +++ b/auth_backend/routes/user.py @@ -148,11 +148,7 @@ async def delete_user( Scopes: `["auth.user.delete"]` or `["auth.self.delete"]` for self delete """ session_scopes = set([scope.name.lower() for scope in current_user.scopes]) - if ( - "auth.user.delete" in session_scopes - or "auth.self.delete" in session_scopes - and user_id == current_user.user_id - ): + if "auth.user.delete" in session_scopes or "auth.self.delete" in session_scopes and user_id == current_user.user_id: logger.debug(f'User id={current_user.id} triggered delete_user') old_user = {"user_id": current_user.id} user: User = User.get(user_id, session=db.session) From d2465ff315da360f650a0dec1a09bbc5f3ea8bab Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Fri, 18 Oct 2024 18:11:01 -0400 Subject: [PATCH 3/5] Fix test --- tests/test_routes/test_user.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_routes/test_user.py b/tests/test_routes/test_user.py index ceea0d8c..bbb59a7c 100644 --- a/tests/test_routes/test_user.py +++ b/tests/test_routes/test_user.py @@ -25,16 +25,18 @@ def test_user_email(client: TestClient, dbsession: Session, user_factory): dbsession.commit() -def test_delete_user(client: TestClient, dbsession: Session, user_factory): - user1 = user_factory(client) +def test_delete_user(client_auth: TestClient, dbsession: Session, user_factory, user_scopes): + token = user_scopes[0] + header = {"Authorization": token} + user1 = user_factory(client_auth) time1 = datetime.utcnow() email_user = AuthMethod(user_id=user1, param="email", auth_method="email", value="testemailx@x.xy") dbsession.add(email_user) dbsession.commit() body = {"name": f"group{time1}", "parent_id": None, "scopes": []} - group = client.post(url="/group", json=body).json()["id"] - client.patch(f"/user/{user1}", json={"groups": [group]}) - resp = client.delete(f"user/{user1}") + group = client_auth.post(url="/group", json=body, headers=header).json()["id"] + client_auth.patch(f"/user/{user1}", json={"groups": [group]}, headers=header) + resp = client_auth.delete(f"user/{user1}", headers=header) assert resp.status_code == 200 user = dbsession.query(User).filter(User.id == user1).one_or_none() assert user.is_deleted From 566b95a239916d209e29e043f9c3c10bad1b522d Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Fri, 18 Oct 2024 18:17:44 -0400 Subject: [PATCH 4/5] Change self del scope name --- auth_backend/routes/user.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/auth_backend/routes/user.py b/auth_backend/routes/user.py index 44a28832..cb2e1a67 100644 --- a/auth_backend/routes/user.py +++ b/auth_backend/routes/user.py @@ -145,10 +145,14 @@ async def delete_user( current_user: UserSession = Depends(UnionAuth(scopes=[], allow_none=False, auto_error=True)), ) -> None: """ - Scopes: `["auth.user.delete"]` or `["auth.self.delete"]` for self delete + Scopes: `["auth.user.delete"]` or `["auth.user.selfdelete"]` for self delete """ session_scopes = set([scope.name.lower() for scope in current_user.scopes]) - if "auth.user.delete" in session_scopes or "auth.self.delete" in session_scopes and user_id == current_user.user_id: + if ( + "auth.user.delete" in session_scopes + or "auth.user.selfdelete" in session_scopes + and user_id == current_user.user_id + ): logger.debug(f'User id={current_user.id} triggered delete_user') old_user = {"user_id": current_user.id} user: User = User.get(user_id, session=db.session) From 7bdacf190749430f2b3a8249abf3ce7f96f24453 Mon Sep 17 00:00:00 2001 From: Timur Enikeev Date: Fri, 18 Oct 2024 18:38:07 -0400 Subject: [PATCH 5/5] Delete route style fix --- auth_backend/routes/user.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/auth_backend/routes/user.py b/auth_backend/routes/user.py index cb2e1a67..dd1aadc4 100644 --- a/auth_backend/routes/user.py +++ b/auth_backend/routes/user.py @@ -148,10 +148,8 @@ async def delete_user( Scopes: `["auth.user.delete"]` or `["auth.user.selfdelete"]` for self delete """ session_scopes = set([scope.name.lower() for scope in current_user.scopes]) - if ( - "auth.user.delete" in session_scopes - or "auth.user.selfdelete" in session_scopes - and user_id == current_user.user_id + if "auth.user.delete" in session_scopes or ( + "auth.user.selfdelete" in session_scopes and user_id == current_user.user_id ): logger.debug(f'User id={current_user.id} triggered delete_user') old_user = {"user_id": current_user.id}