Conversation
| .delete(invitation) | ||
| .where( | ||
| and( | ||
| eq(invitation.organizationId, input.organizationId), |
There was a problem hiding this comment.
Cross-Organization Invitation Deletion
The authorization check validates the caller’s owner/admin role in the active organization, but the deletion uses an arbitrary client-supplied organizationId. An owner or admin can therefore invoke this procedure with another tenant’s ID and delete that tenant’s expired invitations. Bind the deletion to ctx.session.activeOrganizationId or explicitly verify the caller’s role in the requested organization.
How this was verified: The caller’s role is derived from membership in the active organization, while the supplied target organization ID reaches the invitation deletion without any membership or active-organization check.
| api.organization.cleanExpiredInvitations.useMutation({ | ||
| onSuccess: () => { | ||
| toast.success("Expired invitations cleaned successfully"); | ||
| utils.user.all.invalidate(); |
There was a problem hiding this comment.
After cleanup, this invalidates user.all, which reads organization members rather than invitations. The visible invitation list is backed by organization.allInvitations, so successfully deleted invitations remain displayed until another refetch or page reload. Invalidate organization.allInvitations on success.
| utils.user.all.invalidate(); | |
| utils.organization.allInvitations.invalidate(); |
| await db | ||
| .delete(invitation) | ||
| .where( | ||
| and( | ||
| eq(invitation.organizationId, input.organizationId), | ||
| lt(invitation.expiresAt, new Date()), | ||
| ), | ||
| ); |
There was a problem hiding this comment.
This bulk invitation deletion does not emit an audit entry, unlike the existing invitation creation and removal mutations. Cleanup operations will consequently be absent from the organization’s audit trail, making administrative deletion of invitation records untraceable. Record the cleanup through the existing audit utility.
|
Restricted cleanup mutation to strictly authorize admin or owner roles within the target organization (member.organizationId and member.userId). Added standard audit logging via audit(ctx, ...) helper. Updated frontend cache invalidation to target utils.organization.allInvitations.invalidate(). Gated UI button visibility using currentUserRole so regular members cannot see or trigger the action. |
fixes #1413
This PR is not safe to merge until the cleanup mutation is restricted to an organization in which the caller is authorized.
Summary
This PR adds an organization invitation-cleanup mutation and exposes it through a new settings-page action.
Reviews (1) · Last reviewed commit: "feat(org): add admin action to clean up ..."