controlplane/src/core/bufservices/user/removeInvitation.ts (116 lines of code) (raw):
import { PlainMessage } from '@bufbuild/protobuf';
import { HandlerContext } from '@connectrpc/connect';
import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb';
import {
RemoveInvitationRequest,
RemoveInvitationResponse,
} from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb';
import { AuditLogRepository } from '../../repositories/AuditLogRepository.js';
import { OrganizationInvitationRepository } from '../../repositories/OrganizationInvitationRepository.js';
import { OrganizationRepository } from '../../repositories/OrganizationRepository.js';
import { UserRepository } from '../../repositories/UserRepository.js';
import type { RouterOptions } from '../../routes.js';
import { enrichLogger, getLogger, handleError } from '../../util.js';
export function removeInvitation(
opts: RouterOptions,
req: RemoveInvitationRequest,
ctx: HandlerContext,
): Promise<PlainMessage<RemoveInvitationResponse>> {
let logger = getLogger(ctx, opts.logger);
return handleError<PlainMessage<RemoveInvitationResponse>>(ctx, logger, async () => {
const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
logger = enrichLogger(ctx, logger, authContext);
const orgRepo = new OrganizationRepository(logger, opts.db, opts.billingDefaultPlanId);
const orgInvitationRepo = new OrganizationInvitationRepository(logger, opts.db, opts.billingDefaultPlanId);
const userRepo = new UserRepository(logger, opts.db);
const auditLogRepo = new AuditLogRepository(opts.db);
if (!authContext.hasWriteAccess) {
return {
response: {
code: EnumStatusCode.ERR,
details: `The user doesnt have the permissions to perform this operation`,
},
};
}
await opts.keycloakClient.authenticateClient();
const keycloakUser = await opts.keycloakClient.client.users.find({
max: 1,
email: req.email,
realm: opts.keycloakRealm,
exact: true,
});
if (keycloakUser.length === 0) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `User ${req.email} not found`,
},
};
}
const keycloakUserID = keycloakUser[0].id;
const user = await userRepo.byId(keycloakUserID!);
if (!user) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `User ${req.email} not found`,
},
};
}
const org = await orgRepo.byId(authContext.organizationId);
if (!org) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `Organization not found`,
},
};
}
const orgInvitation = await orgInvitationRepo.getPendingOrganizationInvitation({
organizationID: authContext.organizationId,
userID: user.id,
});
if (!orgInvitation) {
return {
response: {
code: EnumStatusCode.ERR_NOT_FOUND,
details: `Invite to the user ${req.email} does not exist.`,
},
};
}
await orgInvitationRepo.removeInvite({
organizationId: authContext.organizationId,
userId: user.id,
});
const userMemberships = await orgRepo.memberships({ userId: user.id });
const userPendingInvitations = await orgInvitationRepo.getPendingInvitationsOfUser({ userId: user.id });
// delete the user only when user doesn't have any memberships and pending invitations
// this will happen only when the user was invited but the user didn't login and the admin removed that user,
// in this case the user will not have a personal org
if (userMemberships.length === 0 && userPendingInvitations.length === 0) {
await userRepo.deleteUser(
{
id: user.id,
keycloakClient: opts.keycloakClient,
keycloakRealm: opts.keycloakRealm,
},
opts.blobStorage,
);
}
await auditLogRepo.addAuditLog({
organizationId: authContext.organizationId,
auditAction: 'organization_invitation.deleted',
action: 'deleted',
actorId: authContext.userId,
auditableDisplayName: user.email,
auditableType: 'user',
actorDisplayName: authContext.userDisplayName,
apiKeyName: authContext.apiKeyName,
targetDisplayName: org.name,
targetType: 'organization',
actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
});
return {
response: {
code: EnumStatusCode.OK,
},
};
});
}