557 lines
19 KiB
TypeScript
557 lines
19 KiB
TypeScript
|
|
import * as plugins from '../plugins.js';
|
||
|
|
import { Reception } from './classes.reception.js';
|
||
|
|
import { UserInvitation } from './classes.userinvitation.js';
|
||
|
|
import { Organization } from './classes.organization.js';
|
||
|
|
import { User } from './classes.user.js';
|
||
|
|
import { Role } from './classes.role.js';
|
||
|
|
|
||
|
|
export class UserInvitationManager {
|
||
|
|
public receptionRef: Reception;
|
||
|
|
public get db() {
|
||
|
|
return this.receptionRef.db.smartdataDb;
|
||
|
|
}
|
||
|
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||
|
|
|
||
|
|
public CUserInvitation = plugins.smartdata.setDefaultManagerForDoc(this, UserInvitation);
|
||
|
|
|
||
|
|
constructor(receptionRefArg: Reception) {
|
||
|
|
this.receptionRef = receptionRefArg;
|
||
|
|
this.receptionRef.typedrouter.addTypedRouter(this.typedrouter);
|
||
|
|
|
||
|
|
this.setupHandlers();
|
||
|
|
}
|
||
|
|
|
||
|
|
private setupHandlers() {
|
||
|
|
// Create invitation
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_CreateInvitation>(
|
||
|
|
'createInvitation',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const email = requestArg.email.toLowerCase().trim();
|
||
|
|
|
||
|
|
// Check if user with this email already exists
|
||
|
|
const existingUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: { email },
|
||
|
|
});
|
||
|
|
if (existingUser) {
|
||
|
|
// User already exists - just add them to the org directly
|
||
|
|
const existingRole = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: existingUser.id,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (existingRole) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
isNew: false,
|
||
|
|
message: 'User is already a member of this organization.',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
// Add user to org with the specified roles
|
||
|
|
await this.receptionRef.roleManager.modifyRoleForUserAtOrg({
|
||
|
|
action: 'create',
|
||
|
|
userId: existingUser.id,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
roles: requestArg.roles,
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
isNew: false,
|
||
|
|
message: 'Existing user has been added to the organization.',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if invitation already exists for this email
|
||
|
|
let invitation = await this.CUserInvitation.getInstance({
|
||
|
|
data: { email },
|
||
|
|
});
|
||
|
|
|
||
|
|
let isNew = false;
|
||
|
|
if (invitation) {
|
||
|
|
// Add org to existing invitation
|
||
|
|
await invitation.addOrganization(requestArg.organizationId, user.id, requestArg.roles);
|
||
|
|
} else {
|
||
|
|
// Create new invitation
|
||
|
|
invitation = await UserInvitation.createNewInvitation(
|
||
|
|
email,
|
||
|
|
requestArg.organizationId,
|
||
|
|
user.id,
|
||
|
|
requestArg.roles
|
||
|
|
);
|
||
|
|
isNew = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Send invitation email
|
||
|
|
await this.sendInvitationEmail(invitation, requestArg.organizationId);
|
||
|
|
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
invitation: await invitation.createSavableObject(),
|
||
|
|
isNew,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get org invitations
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_GetOrgInvitations>(
|
||
|
|
'getOrgInvitations',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const allInvitations = await this.CUserInvitation.getInstances({});
|
||
|
|
const orgInvitations = allInvitations.filter(inv =>
|
||
|
|
inv.data.status === 'pending' &&
|
||
|
|
!inv.isExpired() &&
|
||
|
|
inv.data.organizationRefs.some(ref => ref.organizationId === requestArg.organizationId)
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
invitations: await Promise.all(orgInvitations.map(inv => inv.createSavableObject())),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get org members
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_GetOrgMembers>(
|
||
|
|
'getOrgMembers',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsMemberOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const roles = await this.receptionRef.roleManager.CRole.getInstances({
|
||
|
|
data: { organizationId: requestArg.organizationId },
|
||
|
|
});
|
||
|
|
|
||
|
|
const members: Array<{
|
||
|
|
user: plugins.idpInterfaces.data.IUser;
|
||
|
|
role: plugins.idpInterfaces.data.IRole;
|
||
|
|
}> = [];
|
||
|
|
|
||
|
|
for (const role of roles) {
|
||
|
|
const memberUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
id: role.data.userId,
|
||
|
|
});
|
||
|
|
if (memberUser) {
|
||
|
|
members.push({
|
||
|
|
user: await memberUser.createSavableObject(),
|
||
|
|
role: await role.createSavableObject(),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return { members };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Cancel invitation
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_CancelInvitation>(
|
||
|
|
'cancelInvitation',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const invitation = await this.CUserInvitation.getInstance({ id: requestArg.invitationId });
|
||
|
|
if (!invitation) {
|
||
|
|
return { success: false, message: 'Invitation not found.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
await invitation.removeOrganization(requestArg.organizationId);
|
||
|
|
|
||
|
|
return { success: true };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Resend invitation
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_ResendInvitation>(
|
||
|
|
'resendInvitation',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const invitation = await this.CUserInvitation.getInstance({ id: requestArg.invitationId });
|
||
|
|
if (!invitation) {
|
||
|
|
return { success: false, message: 'Invitation not found.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
await invitation.regenerateToken();
|
||
|
|
await this.sendInvitationEmail(invitation, requestArg.organizationId);
|
||
|
|
|
||
|
|
return { success: true, message: 'Invitation resent.' };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Remove member
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_RemoveMember>(
|
||
|
|
'removeMember',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
// Cannot remove yourself if you're the only owner
|
||
|
|
const role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: requestArg.userId,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
return { success: false, message: 'Member not found.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if trying to remove an owner
|
||
|
|
if (role.data.roles.includes('owner')) {
|
||
|
|
// Count owners
|
||
|
|
const allRoles = await this.receptionRef.roleManager.CRole.getInstances({
|
||
|
|
data: { organizationId: requestArg.organizationId },
|
||
|
|
});
|
||
|
|
const ownerCount = allRoles.filter(r => r.data.roles.includes('owner')).length;
|
||
|
|
if (ownerCount <= 1) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: 'Cannot remove the last owner. Transfer ownership first.',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await role.delete();
|
||
|
|
|
||
|
|
// Remove org from user's connectedOrgs
|
||
|
|
const memberUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
id: requestArg.userId,
|
||
|
|
});
|
||
|
|
if (memberUser && memberUser.data.connectedOrgs) {
|
||
|
|
memberUser.data.connectedOrgs = memberUser.data.connectedOrgs.filter(
|
||
|
|
orgId => orgId !== requestArg.organizationId
|
||
|
|
);
|
||
|
|
await memberUser.save();
|
||
|
|
}
|
||
|
|
|
||
|
|
return { success: true };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Update member roles
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_UpdateMemberRoles>(
|
||
|
|
'updateMemberRoles',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
await this.verifyUserIsAdminOfOrg(user.id, requestArg.organizationId);
|
||
|
|
|
||
|
|
const role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: requestArg.userId,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
return { success: false, message: 'Member not found.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
// If removing owner role, check we're not removing the last owner
|
||
|
|
if (role.data.roles.includes('owner') && !requestArg.roles.includes('owner')) {
|
||
|
|
const allRoles = await this.receptionRef.roleManager.CRole.getInstances({
|
||
|
|
data: { organizationId: requestArg.organizationId },
|
||
|
|
});
|
||
|
|
const ownerCount = allRoles.filter(r => r.data.roles.includes('owner')).length;
|
||
|
|
if (ownerCount <= 1) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: 'Cannot remove owner role from the last owner.',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
role.data.roles = requestArg.roles;
|
||
|
|
await role.save();
|
||
|
|
|
||
|
|
return { success: true, role: await role.createSavableObject() };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Transfer ownership
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_TransferOwnership>(
|
||
|
|
'transferOwnership',
|
||
|
|
async (requestArg) => {
|
||
|
|
const user = await this.receptionRef.userManager.getUserByJwtValidation(requestArg.jwt);
|
||
|
|
|
||
|
|
// Verify current user is an owner
|
||
|
|
const currentUserRole = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: user.id,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (!currentUserRole || !currentUserRole.data.roles.includes('owner')) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError(
|
||
|
|
'Only owners can transfer ownership.'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get new owner's role
|
||
|
|
const newOwnerRole = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: requestArg.newOwnerId,
|
||
|
|
organizationId: requestArg.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (!newOwnerRole) {
|
||
|
|
return { success: false, message: 'New owner must be a member of the organization.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add owner role to new owner
|
||
|
|
if (!newOwnerRole.data.roles.includes('owner')) {
|
||
|
|
newOwnerRole.data.roles.push('owner');
|
||
|
|
await newOwnerRole.save();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove owner role from current user (but keep other roles)
|
||
|
|
currentUserRole.data.roles = currentUserRole.data.roles.filter(r => r !== 'owner');
|
||
|
|
if (currentUserRole.data.roles.length === 0) {
|
||
|
|
currentUserRole.data.roles = ['admin']; // Demote to admin
|
||
|
|
}
|
||
|
|
await currentUserRole.save();
|
||
|
|
|
||
|
|
return { success: true };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get invitation by token
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_GetInvitationByToken>(
|
||
|
|
'getInvitationByToken',
|
||
|
|
async (requestArg) => {
|
||
|
|
const invitation = await this.CUserInvitation.getInstance({
|
||
|
|
data: { token: requestArg.token },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!invitation) {
|
||
|
|
return { isExpired: true, requiresRegistration: false };
|
||
|
|
}
|
||
|
|
|
||
|
|
if (invitation.isExpired()) {
|
||
|
|
return { isExpired: true, requiresRegistration: false };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get organization names
|
||
|
|
const organizations: Array<{ id: string; name: string }> = [];
|
||
|
|
for (const ref of invitation.data.organizationRefs) {
|
||
|
|
const org = await this.receptionRef.organizationmanager.COrganization.getInstance({
|
||
|
|
id: ref.organizationId,
|
||
|
|
});
|
||
|
|
if (org) {
|
||
|
|
organizations.push({ id: org.id, name: org.data.name });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if user with this email exists
|
||
|
|
const existingUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: { email: invitation.data.email },
|
||
|
|
});
|
||
|
|
|
||
|
|
return {
|
||
|
|
invitation: await invitation.createSavableObject(),
|
||
|
|
organizations,
|
||
|
|
isExpired: false,
|
||
|
|
requiresRegistration: !existingUser,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// Accept invitation
|
||
|
|
this.typedrouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_AcceptInvitation>(
|
||
|
|
'acceptInvitation',
|
||
|
|
async (requestArg) => {
|
||
|
|
const invitation = await this.CUserInvitation.getInstance({
|
||
|
|
data: { token: requestArg.token },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!invitation) {
|
||
|
|
return { success: false, message: 'Invalid invitation token.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
if (invitation.isExpired()) {
|
||
|
|
return { success: false, message: 'This invitation has expired.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
const user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
id: requestArg.userId,
|
||
|
|
});
|
||
|
|
if (!user) {
|
||
|
|
return { success: false, message: 'User not found.' };
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create roles for each organization
|
||
|
|
const organizations: plugins.idpInterfaces.data.IOrganization[] = [];
|
||
|
|
const roles: plugins.idpInterfaces.data.IRole[] = [];
|
||
|
|
|
||
|
|
for (const ref of invitation.data.organizationRefs) {
|
||
|
|
// Check if role already exists
|
||
|
|
let role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: {
|
||
|
|
userId: user.id,
|
||
|
|
organizationId: ref.organizationId,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
role = await this.receptionRef.roleManager.modifyRoleForUserAtOrg({
|
||
|
|
action: 'create',
|
||
|
|
userId: user.id,
|
||
|
|
organizationId: ref.organizationId,
|
||
|
|
roles: ref.roles,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
roles.push(await role.createSavableObject());
|
||
|
|
|
||
|
|
const org = await this.receptionRef.organizationmanager.COrganization.getInstance({
|
||
|
|
id: ref.organizationId,
|
||
|
|
});
|
||
|
|
if (org) {
|
||
|
|
// Add role to org's roleIds if not already there
|
||
|
|
if (!org.data.roleIds.includes(role.id)) {
|
||
|
|
org.data.roleIds.push(role.id);
|
||
|
|
await org.save();
|
||
|
|
}
|
||
|
|
organizations.push(await org.createSavableObject());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update user's connectedOrgs
|
||
|
|
if (!user.data.connectedOrgs) {
|
||
|
|
user.data.connectedOrgs = [];
|
||
|
|
}
|
||
|
|
if (!user.data.connectedOrgs.includes(ref.organizationId)) {
|
||
|
|
user.data.connectedOrgs.push(ref.organizationId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await user.save();
|
||
|
|
await invitation.accept(user.id);
|
||
|
|
|
||
|
|
return { success: true, organizations, roles };
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Find invitation by email
|
||
|
|
*/
|
||
|
|
public async getInvitationByEmail(email: string): Promise<UserInvitation | null> {
|
||
|
|
return this.CUserInvitation.getInstance({
|
||
|
|
data: { email: email.toLowerCase().trim() },
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get pending invitations for an email (for registration flow)
|
||
|
|
*/
|
||
|
|
public async getPendingInvitationsForEmail(email: string): Promise<UserInvitation | null> {
|
||
|
|
const invitation = await this.getInvitationByEmail(email);
|
||
|
|
if (invitation && invitation.data.status === 'pending' && !invitation.isExpired()) {
|
||
|
|
return invitation;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clean up expired invitations
|
||
|
|
*/
|
||
|
|
public async cleanupExpiredInvitations(): Promise<number> {
|
||
|
|
const allInvitations = await this.CUserInvitation.getInstances({
|
||
|
|
data: { status: 'pending' },
|
||
|
|
});
|
||
|
|
|
||
|
|
let cleanedCount = 0;
|
||
|
|
for (const invitation of allInvitations) {
|
||
|
|
if (invitation.isExpired()) {
|
||
|
|
invitation.data.status = 'expired';
|
||
|
|
await invitation.save();
|
||
|
|
cleanedCount++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return cleanedCount;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Send invitation email
|
||
|
|
*/
|
||
|
|
private async sendInvitationEmail(
|
||
|
|
invitation: UserInvitation,
|
||
|
|
organizationId: string
|
||
|
|
): Promise<void> {
|
||
|
|
const org = await this.receptionRef.organizationmanager.COrganization.getInstance({
|
||
|
|
id: organizationId,
|
||
|
|
});
|
||
|
|
const orgName = org?.data.name || 'an organization';
|
||
|
|
|
||
|
|
await this.receptionRef.receptionMailer.sendInvitationEmail(
|
||
|
|
invitation.data.email,
|
||
|
|
orgName,
|
||
|
|
invitation.data.token,
|
||
|
|
this.receptionRef.options.baseUrl
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Verify user is admin/owner of organization
|
||
|
|
*/
|
||
|
|
private async verifyUserIsAdminOfOrg(userId: string, organizationId: string): Promise<void> {
|
||
|
|
const role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: { userId, organizationId },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Not a member of this organization.');
|
||
|
|
}
|
||
|
|
|
||
|
|
const hasAdminRole = role.data.roles.some(r =>
|
||
|
|
['owner', 'admin'].includes(r)
|
||
|
|
);
|
||
|
|
|
||
|
|
if (!hasAdminRole) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError(
|
||
|
|
'You do not have permission to perform this action.'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Verify user is member of organization
|
||
|
|
*/
|
||
|
|
private async verifyUserIsMemberOfOrg(userId: string, organizationId: string): Promise<void> {
|
||
|
|
const role = await this.receptionRef.roleManager.CRole.getInstance({
|
||
|
|
data: { userId, organizationId },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!role) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Not a member of this organization.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|