From b87a2c1d179181331d8987231e7c7c8ea75e018d Mon Sep 17 00:00:00 2001 From: oguntayo Date: Sun, 2 Mar 2025 20:36:20 +0100 Subject: [PATCH] feat: added superadmin to deactivate user endpoint --- api/v1/routes/user.py | 76 ++++++- api/v1/schemas/user.py | 10 + hello.db | Bin 0 -> 184320 bytes .../superadmin/test_admin_to_delete_user.py | 206 ++++++++++++++++++ 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 hello.db create mode 100644 tests/v1/superadmin/test_admin_to_delete_user.py diff --git a/api/v1/routes/user.py b/api/v1/routes/user.py index cec064283..fcc7484d7 100644 --- a/api/v1/routes/user.py +++ b/api/v1/routes/user.py @@ -7,11 +7,18 @@ from api.v1.models.user import User from api.v1.schemas.user import ( AllUsersResponse, UserUpdate, - AdminCreateUserResponse, AdminCreateUser + AdminCreateUserResponse, AdminCreateUser,AdminDeleteUserSchema, ) from api.db.database import get_db from api.v1.services.user import user_service +from api.utils.dependencies import get_current_user,get_super_admin + +from fastapi.responses import JSONResponse + +from api.core.dependencies.google_email import mail_service + + user_router = APIRouter(prefix="/users", tags=["Users"]) @@ -209,4 +216,69 @@ def get_user_by_id( user, exclude=['password', 'is_superadmin', 'is_deleted', 'is_verified', 'updated_at', 'created_at', 'is_active'] ) - ) \ No newline at end of file + ) + + + + +@user_router.post('/deactivate', status_code=200) +async def deactivate_account( + request: Request, + schema: AdminDeleteUserSchema, + db: Session = Depends(get_db), + super_admin: User = Depends(user_service.get_current_super_admin) +): + '''Endpoint for super admin to deactivate a user account by user ID''' + + + if not super_admin.is_superadmin: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Only super admins can deactivate users" + ) + + user_to_deactivate = db.query(User).filter(User.id == schema.user_id).first() + + if user_to_deactivate is None: + return JSONResponse( + status_code=404, + content={ + "status": "error", + "status_code": 404, + "message": "User not found", + "data": {} + } + ) + + if not user_to_deactivate.is_active: + return JSONResponse( + status_code=400, + content={ + "status": "error", + "status_code": 400, + "message": "User is already deactivated", + "data": {} + } + ) + + user_to_deactivate.is_active = False + + # Send email to user + mail_service.send_mail( + to=user_to_deactivate.email, + subject='Account Deactivation', + body=f'Hello {user_to_deactivate.first_name},\n\nYour account has been deactivated successfully.\n\nTo reactivate your account, visit:\n{request.url.hostname}/api/v1/users/accounts/reactivate\n\nThis link will expire in 15 minutes.' + ) + + db.commit() + + + return JSONResponse( + status_code=200, + content={ + "status": "success", + "status_code": 200, + "message": "User account deactivated successfully", + "data": {} + } + ) diff --git a/api/v1/schemas/user.py b/api/v1/schemas/user.py index 095135e11..2cbd07511 100644 --- a/api/v1/schemas/user.py +++ b/api/v1/schemas/user.py @@ -438,3 +438,13 @@ def role_validator(cls, value): if value not in ["admin", "user", "guest", "owner"]: raise ValueError("Role has to be one of admin, guest, user, or owner") return value + + + + +class AdminDeleteUserSchema(BaseModel): + """Schema for admin for deleting a user account""" + + user_id: str = Field(..., title="User ID", description="The ID of the user to be deleted") + + diff --git a/hello.db b/hello.db new file mode 100644 index 0000000000000000000000000000000000000000..ff89a5f2f3d464e5246a1313ae422cd6ccd2ae2c GIT binary patch literal 184320 zcmeI)%X8bt0mpGd4^X1@u#+f=V<(0pD-xrq4eil*9FN<|GMj25OR*kloKAy*NLa;~ zFB702y`-{|v_18lzo3`)*v|CUQ~M8eIz6nQi{))tqt!5I_I{1Q0*~0R#|0009ILc=iIXPECqVPe1#f=@9`0 z5I_I{1Q0*~0R#|0009K#5y*(4aQ`n)AdCb71Q0*~0R#|0009ILKmdUR1bF_R04iM~ zfB*srAb-O zgyA5700IagfB*srAbgVZSs@F$us6(T_AO2#<8UFcjZAeS~Mfqc@c&cX@An{nqOC9sS+>9iJeRU6@q0Ycop9M|AF&Y}Yc(hFkTwo1{6R zGPQq7)h;h6k3-sqUAf<|>$cTm54I<|_y@IKNUb1(vSuRl<7sVSLHT67ok-BY8$_^? ziDwj4>5G|ht8Ch(uFQ8ErIJxG%U0LgvgJ7Dfz=tLWT9QnsM@WC6G96(43S>2dFtL+ zTD!8O94hS`eJjJY%C(X=n?#balJR(!vHDh;vC?LqJ8D{6T2emwzMp5TNo+G6$uw3e zQgv;&)Fqqt&^uFeAJ@9)+}_1-T+?kh?YXdcC42myTc}n_cEwtd@GW~it!lTIdZizO zGjg$lvG+$+ZFyEHwfGYqPV;r_+AeL2dAABoc8@ zF47fS2lZ;B)=ndGdW;4YsVbR{YZQK974BKZr#^SqEvHtkIF{ks-nn3N?^b?$eNALt zv|R7p;rNqD!t5`PsMLC&rqnpY$khs=%mRV?<^N9 z81^nx#>AxxpZ}Nef{h^|fB*srAbhDv(nz}Rbhl$T8K05X9%zrb#%ls^JdHn6Me~tY_{oB~J z^gq<{=!kkl)kc0h^w*Ie4;?AXLvMRK_VMfEH`3bbigGy9UXkNk^|E7_m7-CoR*JS* zb|}_mbt_(uuhQw(sI^|FTUB%APG&K!t*j_VSNxSb-P(04TZ*g{>DD4xwbfcRDOS{Y zzmscKTv=DxypvV6@2&J&VbkrmG;-Za-~4Dn)pGMnBS_UO8)a|ljkoN^Pcztjqcg>z z@;JSq$WQHPc0R4m&nusewo(h~T`PFK7)vRr5$W_?3R$muYwfzNbuC&>p>EgwwXhuv zUTb#2ZPYEJQSZL|(jQoOS)P~>cg=#eU#;FVyajjn+Q;#POO(?nB%4 z)H^C9Tc~;Ebya(JzV~Db!Vr@v*vj6xsA_YXa;H6cL>yw)_&*oo)1_T2R@M5in{VsW zvuRD!l%tRR3F24y|G5~M60IFhm<#d9{cId#zhReLyQ1IN+Se zy_eS)#XR>`mu2Tv?Yh=04`0H#hSvw03n_IUEh9hW)@SJT_|8l3i#|jA+p- zozo)P;CNC*i~T9_&2y@@v)r2?x{KkBJz8@9@WIPzZFyNay3m?a(fWA%bFujpZ7pU~ z`0ssWk*HL&DU<#1tg3x|PPu=aly?1SNhdD=(?q`drUc zg4W)Q3^vZLXivLaF0|WAhd+EVthn*(Yc8_u3r9oG z`JayYbj^=7QW@EJJ`!!D>crE#|4D)91z54+xY5rq#joDN7YC86{6;0Vr&z;(d`&5$ zGmAxU9scphFGP`4=Y;U|@@>57_2HT5S)yV+bV`=E78PfS(AIPDsTHbgO{`E!^DJ@t zq_=yhwUG;LoOyC)y7_ju{dp0p3cWlXnG>Okj<@?_?ya|?tNFtbi%Wjt5SAfB*srAbSAfB*srAb