feat: sensical user invitation
Some checks failed
ci / lint_and_test (push) Failing after 8s

Users can now be invited to an org by email.

"Email" for now is "print to stdout"

Resolves #12
This commit is contained in:
Chris Milne 2026-06-09 12:22:36 +01:00
parent 1012947b67
commit 62c43ce883
5 changed files with 173 additions and 5 deletions

View file

@ -8,18 +8,30 @@ Endpoints:
- [DELETE](/user/): [super admin]: Removes a User(id) from the hub database.
"""
from fastapi import APIRouter
from starlette import status
from fastapi import APIRouter, status, BackgroundTasks
from src.user.schemas import UserResponse, OIDCClaims
from auth.exceptions import UnauthorizedException
from organisation.exceptions import OrgNotFoundException
from src.user.schemas import (
UserResponse,
OIDCClaims,
UserPostInvitationRequest,
UserPostInvitationAcceptRequest,
)
from src.user.dependencies import (
user_model_claims_dependency,
user_model_query_dependency,
)
from src.user.service import send_invitation
from src.organisation.models import Organisation as Org
from src.auth.dependencies import super_admin_dependency
from src.auth.dependencies import (
super_admin_dependency,
org_model_root_claim_body_dependency,
)
from src.auth.service import claims_dependency
from src.database import db_dependency
from src.utils import decode_jwt
router = APIRouter(
prefix="/user",
@ -99,3 +111,50 @@ async def delete_user_by_id(
"""
db.delete(user_model)
db.commit()
@router.post(
"/invitation",
summary="Send an email invitation for a user to join an org",
status_code=status.HTTP_200_OK,
)
async def invitation(
background_tasks: BackgroundTasks,
org_model: org_model_root_claim_body_dependency,
request_model: UserPostInvitationRequest,
):
org_id = org_model.id
org_name = org_model.name
user_email = request_model.user_email
background_tasks.add_task(
send_invitation, org_id=org_id, org_name=org_name, user_email=user_email
)
return "Invitation sent"
@router.post(
"/invitation/accept",
summary="Accept email invitation to join an org",
status_code=status.HTTP_200_OK,
)
async def accept_invitation(
db: db_dependency,
user_model: user_model_claims_dependency,
request_model: UserPostInvitationAcceptRequest,
):
email_claims = await decode_jwt(request_model.jwt)
claimed_email = email_claims["user_email"]
if user_model.email != claimed_email:
raise UnauthorizedException("The logged in user and email do not match.")
org_model = db.get(Org, email_claims["org_id"])
if org_model is None:
raise OrgNotFoundException()
org_model.user_rel.append(user_model)
db.commit()
return "Invitation accepted"