feat: more accurate status codes

403 Forbidden replacing many 401 Unauthorized usages.
This commit is contained in:
Chris Milne 2026-06-11 14:58:05 +01:00
parent b3ae655009
commit c2e035dede
11 changed files with 81 additions and 74 deletions

View file

@ -23,9 +23,12 @@ from fastapi import APIRouter, status
from fastapi.params import Query
from sqlalchemy.exc import IntegrityError
from src.auth.exceptions import UnauthorizedException
from src.contact.schemas import ContactModel
from src.exceptions import UnprocessableContentException, ConflictException
from src.exceptions import (
UnprocessableContentException,
ConflictException,
ForbiddenException,
)
from src.contact.models import Contact
from src.contact.schemas import ContactAddress
from src.contact.exceptions import ContactNotFoundException
@ -86,7 +89,7 @@ router = APIRouter(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Missing or invalid org_id query parameter"
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},
@ -204,7 +207,7 @@ async def create_org(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Invalid data in request."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},
@ -259,7 +262,7 @@ async def update_questionnaire(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Invalid data in request."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be super admin."
},
},
@ -290,7 +293,7 @@ async def update_status(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Org ID missing or invalid."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},
@ -314,7 +317,7 @@ async def get_users(org_model: org_model_root_claim_query_dependency):
status.HTTP_200_OK: {
"description": "Successfully added user to the organisation."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
status.HTTP_422_UNPROCESSABLE_CONTENT: {
@ -355,7 +358,7 @@ async def add_user_to_org(
status.HTTP_204_NO_CONTENT: {
"description": "Successfully deleted organisation."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be super admin."
},
status.HTTP_422_UNPROCESSABLE_CONTENT: {
@ -383,12 +386,22 @@ async def delete_organisation_by_id(
status.HTTP_204_NO_CONTENT: {
"description": "Successfully deleted organisation."
},
status.HTTP_401_UNAUTHORIZED: {
"description": "Not authorised. Must be root user."
},
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Org ID missing or invalid."
},
status.HTTP_403_FORBIDDEN: {
"description": "Forbidden",
"content": {
"application/json": {
"examples": {
"invalid_state": {
"summary": "Organisation is no longer in pre-approval state."
},
"not_root": {"summary": "Not authorised. Must be root user."},
}
}
},
},
},
)
async def delete_preapproved_organisation_by_id(
@ -400,7 +413,7 @@ async def delete_preapproved_organisation_by_id(
"""
org_status = StatusEnum(org_model.status)
if not org_status.is_pre_approval:
raise UnauthorizedException(
raise ForbiddenException(
message="Organisation is no longer in pre-approval state."
)
@ -456,7 +469,7 @@ async def update_root_user(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Org ID missing or invalid."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},
@ -479,7 +492,7 @@ async def get_org_groups(org_model: org_model_root_claim_query_dependency):
status_code=status.HTTP_204_NO_CONTENT,
responses={
status.HTTP_204_NO_CONTENT: {"description": "Successfully removed user."},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
status.HTTP_422_UNPROCESSABLE_CONTENT: {
@ -512,7 +525,7 @@ async def remove_user_from_org(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Invalid data in request."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},
@ -557,7 +570,7 @@ async def get_contact(
status.HTTP_422_UNPROCESSABLE_CONTENT: {
"description": "Invalid data in request."
},
status.HTTP_401_UNAUTHORIZED: {
status.HTTP_403_FORBIDDEN: {
"description": "Not authorised. Must be org root user."
},
},