feat: govern attachment exceptions and ownership transfers
This commit is contained in:
@@ -35,7 +35,6 @@ from govoplan_core.security.time import utc_now
|
||||
|
||||
from govoplan_campaign.backend.route_support import (
|
||||
_access_directory,
|
||||
_clear_current_version_mail_profile_for_owner_transfer,
|
||||
_get_campaign_for_principal,
|
||||
)
|
||||
|
||||
@@ -170,59 +169,20 @@ def update_campaign_owner(
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(require_scope("campaigns:campaign:share")),
|
||||
):
|
||||
campaign = _get_campaign_for_principal(session, campaign_id, principal, write=True)
|
||||
if payload.owner_user_id and payload.owner_group_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail="Choose either a user owner or a group owner, not both",
|
||||
)
|
||||
directory = _access_directory()
|
||||
if payload.owner_user_id:
|
||||
owner = directory.get_user(payload.owner_user_id)
|
||||
if owner is not None and (
|
||||
owner.tenant_id != principal.tenant_id or owner.status != "active"
|
||||
):
|
||||
owner = None
|
||||
if owner is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Owner user not found"
|
||||
)
|
||||
if payload.owner_group_id:
|
||||
group = directory.get_group(payload.owner_group_id)
|
||||
if group is not None and (
|
||||
group.tenant_id != principal.tenant_id or group.status != "active"
|
||||
):
|
||||
group = None
|
||||
if group is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="Owner group not found"
|
||||
)
|
||||
owner_changed = (
|
||||
campaign.owner_user_id != payload.owner_user_id
|
||||
or campaign.owner_group_id != payload.owner_group_id
|
||||
)
|
||||
mail_profile_reselection_required = False
|
||||
if owner_changed:
|
||||
mail_profile_reselection_required = (
|
||||
_clear_current_version_mail_profile_for_owner_transfer(session, campaign)
|
||||
)
|
||||
|
||||
campaign.owner_user_id = payload.owner_user_id
|
||||
campaign.owner_group_id = payload.owner_group_id
|
||||
session.add(campaign)
|
||||
audit_from_principal(
|
||||
del payload
|
||||
_get_campaign_for_principal(
|
||||
session,
|
||||
campaign_id,
|
||||
principal,
|
||||
action="campaign.owner_updated",
|
||||
object_type="campaign",
|
||||
object_id=campaign.id,
|
||||
details={
|
||||
**payload.model_dump(),
|
||||
"mail_profile_reselection_required": mail_profile_reselection_required,
|
||||
},
|
||||
commit=True,
|
||||
write=True,
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=(
|
||||
"Direct campaign owner mutation has been retired. Use the "
|
||||
"ownership transfer workflow so the target can accept."
|
||||
),
|
||||
)
|
||||
return CampaignResponse.model_validate(campaign)
|
||||
|
||||
|
||||
@router.post(
|
||||
|
||||
@@ -439,6 +439,10 @@ def set_version_review_state(
|
||||
version_id=version_id,
|
||||
inspection_complete=payload.inspection_complete,
|
||||
reviewed_message_keys=payload.reviewed_message_keys,
|
||||
issue_decisions=[
|
||||
item.model_dump()
|
||||
for item in payload.issue_decisions
|
||||
],
|
||||
user_id=principal.user.id,
|
||||
commit=False,
|
||||
)
|
||||
@@ -452,6 +456,7 @@ def set_version_review_state(
|
||||
"campaign_id": campaign_id,
|
||||
"inspection_complete": payload.inspection_complete,
|
||||
"reviewed_message_count": len(payload.reviewed_message_keys),
|
||||
"issue_decision_count": len(payload.issue_decisions),
|
||||
},
|
||||
commit=True,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user