Skip to content

feat(org): add admin action to clean up expired invitations (#1413) - #5483

Open
KyngPyng wants to merge 2 commits into
Dokploy:canaryfrom
KyngPyng:feat/cleanup-expired-invitations
Open

KyngPyng wants to merge 2 commits into
Dokploy:canaryfrom
KyngPyng:feat/cleanup-expired-invitations

Conversation

@KyngPyng

@KyngPyng KyngPyng commented Sep 19, 2026

Copy link
Copy Markdown

fixes #1413

RetriggerConfidence Score: 4/5

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.

  • Deletes expired invitation rows for a selected organization.
  • Adds a “Clean Expired” button and success/error notifications.
  • The current implementation permits cross-organization deletion, leaves the invitation cache stale, exposes the action to unauthorized users, and omits audit logging.

Reviews (1) · Last reviewed commit: "feat(org): add admin action to clean up ..."

.delete(invitation)
.where(
and(
eq(invitation.organizationId, input.organizationId),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Invitation Cache Stays Stale

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.

Suggested change
utils.user.all.invalidate();
utils.organization.allInvitations.invalidate();

Comment thread apps/dokploy/components/dashboard/settings/users/show-users.tsx Outdated
Comment on lines +37 to +44
await db
.delete(invitation)
.where(
and(
eq(invitation.organizationId, input.organizationId),
lt(invitation.expiresAt, new Date()),
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Cleanup Bypasses Audit Trail

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.

@KyngPyng

Copy link
Copy Markdown
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Organisation and Teams Management

1 participant