Skip to content

Commit e21d4fd

Browse files
feat: add admin route for capturing revoke reasonings
1 parent df86d61 commit e21d4fd

File tree

1 file changed

+90
-1
lines changed

1 file changed

+90
-1
lines changed

routers/admin.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from fastapi import APIRouter, Depends, HTTPException, Path
66
from fastapi.params import Query
7-
from pydantic import BaseModel, Field, ValidationError
7+
from sqlalchemy import alias, false, func, or_
8+
from pydantic import BaseModel, Field, ValidationError, field_validator
89
from sqlalchemy import alias, false, func, or_
910
from sqlmodel import Session, select
1011

@@ -81,6 +82,18 @@ def get_pagination_params(page: int = 1, per_page: int = 100):
8182
dependencies=[Depends(user_is_admin)])
8283

8384

85+
class RevokeServiceRequest(BaseModel):
86+
reason: Annotated[str | None, Field(default=None, max_length=1024)] = None
87+
88+
@field_validator("reason")
89+
@classmethod
90+
def strip_reason(cls, value: str | None) -> str | None:
91+
if value is None:
92+
return None
93+
stripped = value.strip()
94+
return stripped or None
95+
96+
8497
@router.get("/filters")
8598
def get_filter_options():
8699
"""
@@ -278,3 +291,79 @@ def resend_verification_email(user_id: Annotated[str, UserIdParam],
278291
client: Annotated[Auth0Client, Depends(get_auth0_client)]):
279292
client.resend_verification_email(user_id)
280293
return {"message": "Verification email resent."}
294+
295+
296+
@router.post("/users/{user_id}/services/{service_id}/approve")
297+
def approve_service(user_id: Annotated[str, UserIdParam],
298+
service_id: Annotated[str, ServiceIdParam],
299+
client: Annotated[Auth0Client, Depends(get_auth0_client)],
300+
approving_user: Annotated[SessionUser, Depends(get_current_user)]):
301+
user = client.get_user(user_id=user_id)
302+
# Need to fetch full user info currently to get email address, not in access token
303+
approving_user_data = client.get_user(user_id=approving_user.access_token.sub)
304+
logger.debug(f"Approving service {service_id} for user {user_id} by {approving_user_data.email}")
305+
user.app_metadata.approve_service(service_id, updated_by=str(approving_user_data.email))
306+
logger.info("Sending updated metadata to Auth0 API")
307+
# update_user_metadata is async, so run via asyncio
308+
update = update_user_metadata(
309+
user_id=user_id,
310+
token=client.management_token,
311+
metadata=user.app_metadata.model_dump(mode="json")
312+
)
313+
resp = asyncio.run(update)
314+
logger.info("Metadata updated successfully")
315+
return resp
316+
317+
318+
@router.post("/users/{user_id}/services/{service_id}/revoke")
319+
def revoke_service(user_id: Annotated[str, UserIdParam],
320+
service_id: Annotated[str, ServiceIdParam],
321+
payload: RevokeServiceRequest,
322+
client: Annotated[Auth0Client, Depends(get_auth0_client)],
323+
revoking_user: Annotated[SessionUser, Depends(get_current_user)]):
324+
"""
325+
Revoke a service and all associated resources for a user.
326+
"""
327+
user = client.get_user(user_id=user_id)
328+
revoking_user_data = client.get_user(user_id=revoking_user.access_token.sub)
329+
user.app_metadata.revoke_service(
330+
service_id=service_id,
331+
updated_by=str(revoking_user_data.email),
332+
reason=payload.reason,
333+
)
334+
service = user.app_metadata.get_service_by_id(service_id)
335+
if service is None:
336+
raise HTTPException(status_code=404, detail=f"Service '{service_id}' not found for user '{user_id}'")
337+
for resource in service.resources:
338+
resource.revoke()
339+
update = update_user_metadata(
340+
user_id=user_id,
341+
token=client.management_token,
342+
metadata=user.app_metadata.model_dump(mode="json")
343+
)
344+
resp = asyncio.run(update)
345+
return resp
346+
347+
348+
@router.post("/users/{user_id}/services/{service_id}/resources/{resource_id}/approve")
349+
def approve_resource(user_id: Annotated[str, UserIdParam],
350+
service_id: Annotated[str, ServiceIdParam],
351+
resource_id: Annotated[str, ResourceIdParam],
352+
client: Annotated[Auth0Client, Depends(get_auth0_client)],
353+
approving_user: Annotated[SessionUser, Depends(get_current_user)]):
354+
user = client.get_user(user_id=user_id)
355+
approving_user_data = client.get_user(user_id=approving_user.access_token.sub)
356+
357+
user.app_metadata.approve_resource(
358+
service_id=service_id,
359+
resource_id=resource_id,
360+
updated_by=approving_user_data.email
361+
)
362+
363+
update = update_user_metadata(
364+
user_id=user_id,
365+
token=client.management_token,
366+
metadata=user.app_metadata.model_dump(mode="json")
367+
)
368+
resp = asyncio.run(update)
369+
return resp

0 commit comments

Comments
 (0)