feat: root user verification

New root users must already be members of the organisation.
This commit is contained in:
Chris Milne 2026-05-29 09:52:34 +01:00
parent da5099e172
commit 8e8c00c34c

View file

@ -15,21 +15,21 @@ Endpoints:
- [GET](/org/contact): [root user]: Gets the (contact_type) contact for an org(id) - [GET](/org/contact): [root user]: Gets the (contact_type) contact for an org(id)
- [PATCH](/org/contact): [root user]: Updates the (contact_type) contact for an org(id). Any number of details can be changed. - [PATCH](/org/contact): [root user]: Updates the (contact_type) contact for an org(id). Any number of details can be changed.
""" """
from typing import Annotated, Optional from typing import Annotated
from fastapi import APIRouter, status from fastapi import APIRouter, status
from fastapi.params import Query from fastapi.params import Query
from psycopg.errors import UniqueViolation from psycopg.errors import UniqueViolation
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from contact.schemas import ContactModel from src.auth.exceptions import UnauthorizedException
from src.exceptions import UnprocessableContent, Conflict from src.contact.schemas import ContactModel
from src.exceptions import UnprocessableContentException, ConflictException
from src.contact.models import Contact from src.contact.models import Contact
from src.contact.schemas import ContactAddress from src.contact.schemas import ContactAddress
from src.contact.exceptions import ContactNotFoundException from src.contact.exceptions import ContactNotFoundException
from src.database import db_dependency from src.database import db_dependency
from src.user.models import User from src.user.dependencies import user_model_body_dependency, user_model_claims_dependency
from user.dependencies import user_model_body_dependency, user_model_claims_dependency
from src.auth.dependencies import super_admin_dependency, org_model_root_claim_query_dependency, org_model_root_claim_body_dependency from src.auth.dependencies import super_admin_dependency, org_model_root_claim_query_dependency, org_model_root_claim_body_dependency
from src.organisation.dependencies import org_model_body_dependency from src.organisation.dependencies import org_model_body_dependency
@ -94,7 +94,7 @@ async def create_org(db: db_dependency, user_model: user_model_claims_dependency
db.flush() db.flush()
except IntegrityError as e: except IntegrityError as e:
if isinstance(e.orig, UniqueViolation): if isinstance(e.orig, UniqueViolation):
raise Conflict(message="Organisation with this name already exists") raise ConflictException(message="Organisation with this name already exists")
# Adds currently logged-in user to org users list and sets them as root_user # Adds currently logged-in user to org users list and sets them as root_user
org_model.user_rel.append(user_model) org_model.user_rel.append(user_model)
org_model.root_user_rel = user_model org_model.root_user_rel = user_model
@ -177,7 +177,7 @@ async def get_users(org_model: org_model_root_claim_query_dependency):
}) })
async def add_user_to_org(db: db_dependency, org_model: org_model_root_claim_body_dependency, user_model: user_model_body_dependency, request_model: OrgPostUserRequest): async def add_user_to_org(db: db_dependency, org_model: org_model_root_claim_body_dependency, user_model: user_model_body_dependency, request_model: OrgPostUserRequest):
if user_model in org_model.user_rel: if user_model in org_model.user_rel:
raise Conflict(message="User already a part of this organisation") raise ConflictException(message="User already a part of this organisation")
org_model.user_rel.append(user_model) org_model.user_rel.append(user_model)
db.flush() db.flush()
response = {"users": [user.email for user in org_model.user_rel]} response = {"users": [user.email for user in org_model.user_rel]}
@ -208,7 +208,8 @@ async def delete_organisation_by_id(db: db_dependency, org_model: org_model_body
status.HTTP_401_UNAUTHORIZED: {"description": "Not authorised. Must be super admin."}, status.HTTP_401_UNAUTHORIZED: {"description": "Not authorised. Must be super admin."},
}) })
async def update_root_user(db: db_dependency, org_model: org_model_body_dependency, user_model: user_model_body_dependency, su: super_admin_dependency, request_model: OrgPatchRootRequest): async def update_root_user(db: db_dependency, org_model: org_model_body_dependency, user_model: user_model_body_dependency, su: super_admin_dependency, request_model: OrgPatchRootRequest):
# TODO: verify new user is already an org member if user_model not in org_model.user_rel:
raise UnauthorizedException(message="This user does not belong to your organisation.")
org_model.root_user_rel = user_model org_model.root_user_rel = user_model
db.flush() db.flush()
response = OrgPatchRootResponse(**org_model.__dict__) response = OrgPatchRootResponse(**org_model.__dict__)
@ -263,7 +264,7 @@ async def get_contact(org_model: org_model_root_claim_query_dependency, contact_
case "owner": case "owner":
contact_model = org_model.owner_contact_rel contact_model = org_model.owner_contact_rel
case _: case _:
raise UnprocessableContent("Invalid contact type") raise UnprocessableContentException("Invalid contact type")
if contact_model is None: if contact_model is None:
raise ContactNotFoundException() raise ContactNotFoundException()
@ -292,7 +293,7 @@ async def update_contact(db: db_dependency, org_model: org_model_root_claim_body
case "owner": case "owner":
contact_model = org_model.owner_contact_rel contact_model = org_model.owner_contact_rel
case _: case _:
raise UnprocessableContent("Invalid contact type") raise UnprocessableContentException("Invalid contact type")
if contact_model is None: if contact_model is None:
raise ContactNotFoundException() raise ContactNotFoundException()
@ -302,7 +303,7 @@ async def update_contact(db: db_dependency, org_model: org_model_root_claim_body
if hasattr(contact_model, key): if hasattr(contact_model, key):
setattr(contact_model, key, value) setattr(contact_model, key, value)
else: else:
raise UnprocessableContent("Invalid keys in update request") raise UnprocessableContentException("Invalid keys in update request")
db.flush() db.flush()
address = ContactAddress.model_validate(contact_model) address = ContactAddress.model_validate(contact_model)