feat: added saml parsing attributes and injecting to metadata of a user in org scoped

This commit is contained in:
=
2024-09-25 21:37:25 +05:30
parent 77e191d63e
commit 4928322cdb
15 changed files with 164 additions and 59 deletions

View File

@@ -8,13 +8,12 @@ export async function up(knex: Knex): Promise<void> {
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
tb.string("key").notNullable(); tb.string("key").notNullable();
tb.string("value").notNullable(); tb.string("value").notNullable();
tb.uuid("userOrgMembershipId"); tb.uuid("orgId").notNullable();
tb.foreign("userOrgMembershipId").references("id").inTable(TableName.OrgMembership).onDelete("CASCADE"); tb.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
tb.uuid("identityOrgMembershipId"); tb.uuid("userId");
tb.foreign("identityOrgMembershipId") tb.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
.references("id") tb.uuid("identityId");
.inTable(TableName.IdentityOrgMembership) tb.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
.onDelete("CASCADE");
tb.timestamps(true, true, true); tb.timestamps(true, true, true);
}); });
} }

View File

@@ -11,8 +11,9 @@ export const IdentityMetadataSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
key: z.string(), key: z.string(),
value: z.string(), value: z.string(),
userOrgMembershipId: z.string().uuid().nullable().optional(), orgId: z.string().uuid(),
identityOrgMembershipId: z.string().uuid().nullable().optional(), userId: z.string().uuid().nullable().optional(),
identityId: z.string().uuid().nullable().optional(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date() updatedAt: z.date()
}); });

View File

@@ -100,6 +100,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
async (req, profile, cb) => { async (req, profile, cb) => {
try { try {
if (!profile) throw new BadRequestError({ message: "Missing profile" }); if (!profile) throw new BadRequestError({ message: "Missing profile" });
const email = const email =
profile?.email ?? profile?.email ??
// entra sends data in this format // entra sends data in this format
@@ -123,6 +124,14 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
); );
} }
const userMetadata = Object.keys(profile.attributes || {})
.map((key) => {
// for the ones like in format: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email
const formatedKey = key.startsWith("http") ? key.split("/").at(-1) || "" : key;
return { key: formatedKey, value: String((profile.attributes as Record<string, string>)[key]) };
})
.filter((el) => el.key && !["email", "firstName", "lastName"].includes(el.key));
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({ const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
externalId: profile.nameID, externalId: profile.nameID,
email, email,
@@ -130,7 +139,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
lastName: lastName as string, lastName: lastName as string,
relayState: (req.body as { RelayState?: string }).RelayState, relayState: (req.body as { RelayState?: string }).RelayState,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string,
metadata: userMetadata
}); });
cb(null, { isUserCompleted, providerAuthToken }); cb(null, { isUserCompleted, providerAuthToken });
} catch (error) { } catch (error) {

View File

@@ -21,14 +21,14 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
ipAllowlisting: false, ipAllowlisting: false,
rbac: false, rbac: true,
customRateLimits: false, customRateLimits: false,
customAlerts: false, customAlerts: false,
auditLogs: false, auditLogs: false,
auditLogsRetentionDays: 0, auditLogsRetentionDays: 0,
auditLogStreams: false, auditLogStreams: false,
auditLogStreamLimit: 3, auditLogStreamLimit: 3,
samlSSO: false, samlSSO: true,
oidcSSO: false, oidcSSO: false,
scim: false, scim: false,
ldap: false, ldap: false,

View File

@@ -168,6 +168,11 @@ export const permissionDALFactory = (db: TDbClient) => {
}) })
.join<TProjects>(TableName.Project, `${TableName.Project}.id`, db.raw("?", [projectId])) .join<TProjects>(TableName.Project, `${TableName.Project}.id`, db.raw("?", [projectId]))
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder
.on(`${TableName.Users}.id`, `${TableName.IdentityMetadata}.userId`)
.andOn(`${TableName.Organization}.id`, `${TableName.IdentityMetadata}.orgId`);
})
.select( .select(
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("username").withSchema(TableName.Users).as("username"), db.ref("username").withSchema(TableName.Users).as("username"),
@@ -258,6 +263,9 @@ export const permissionDALFactory = (db: TDbClient) => {
.withSchema(TableName.ProjectUserAdditionalPrivilege) .withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userAdditionalPrivilegesTemporaryAccessEndTime"), .as("userAdditionalPrivilegesTemporaryAccessEndTime"),
// general // general
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue"),
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("orgId").withSchema(TableName.Project), db.ref("orgId").withSchema(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("projectId") db.ref("id").withSchema(TableName.Project).as("projectId")
@@ -357,6 +365,15 @@ export const permissionDALFactory = (db: TDbClient) => {
temporaryAccessEndTime: userAdditionalPrivilegesTemporaryAccessEndTime, temporaryAccessEndTime: userAdditionalPrivilegesTemporaryAccessEndTime,
isTemporary: userAdditionalPrivilegesIsTemporary isTemporary: userAdditionalPrivilegesIsTemporary
}) })
},
{
key: "metadataId",
label: "metadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
} }
] ]
}); });
@@ -419,6 +436,11 @@ export const permissionDALFactory = (db: TDbClient) => {
`${TableName.IdentityProjectMembership}.projectId`, `${TableName.IdentityProjectMembership}.projectId`,
`${TableName.Project}.id` `${TableName.Project}.id`
) )
.leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
void queryBuilder
.on(`${TableName.Identity}.id`, `${TableName.IdentityMetadata}.identityId`)
.andOn(`${TableName.Project}.orgId`, `${TableName.IdentityMetadata}.orgId`);
})
.where("identityId", identityId) .where("identityId", identityId)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId) .where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.IdentityProjectMembershipRole)) .select(selectAllTableCols(TableName.IdentityProjectMembershipRole))
@@ -448,7 +470,10 @@ export const permissionDALFactory = (db: TDbClient) => {
db db
.ref("temporaryAccessEndTime") .ref("temporaryAccessEndTime")
.withSchema(TableName.IdentityProjectAdditionalPrivilege) .withSchema(TableName.IdentityProjectAdditionalPrivilege)
.as("identityApTemporaryAccessEndTime") .as("identityApTemporaryAccessEndTime"),
db.ref("id").withSchema(TableName.IdentityMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.IdentityMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue")
); );
const permission = sqlNestRelationships({ const permission = sqlNestRelationships({
@@ -495,6 +520,15 @@ export const permissionDALFactory = (db: TDbClient) => {
temporaryAccessStartTime: identityApTemporaryAccessStartTime, temporaryAccessStartTime: identityApTemporaryAccessStartTime,
isTemporary: identityApIsTemporary isTemporary: identityApIsTemporary
}) })
},
{
key: "metadataId",
label: "metadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
} }
] ]
}); });

View File

@@ -12,6 +12,7 @@ import {
} from "@app/db/schemas"; } from "@app/db/schemas";
import { conditionsMatcher } from "@app/lib/casl"; import { conditionsMatcher } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { objectify } from "@app/lib/fn";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type"; import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal"; import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -223,9 +224,20 @@ export const permissionServiceFactory = ({
})) || []; })) || [];
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges)); const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false }); const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false, strict: true });
const metadataKeyValuePair = objectify(
userProjectPermission.metadata,
(i) => i.key,
(i) => i.value
);
const interpolateRules = templatedRules( const interpolateRules = templatedRules(
{ identity: { id: userProjectPermission.userId, username: userProjectPermission.username } }, {
identity: {
id: userProjectPermission.userId,
username: userProjectPermission.username,
metadata: metadataKeyValuePair
}
},
{ data: false } { data: false }
); );
const permission = createMongoAbility<ProjectPermissionSet>( const permission = createMongoAbility<ProjectPermissionSet>(
@@ -275,9 +287,20 @@ export const permissionServiceFactory = ({
})) || []; })) || [];
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges)); const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false }); const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false, strict: true });
const metadataKeyValuePair = objectify(
identityProjectPermission.metadata,
(i) => i.key,
(i) => i.value
);
const interpolateRules = templatedRules( const interpolateRules = templatedRules(
{ identity: { id: identityProjectPermission.identityId, username: identityProjectPermission.username } }, {
identity: {
id: identityProjectPermission.identityId,
username: identityProjectPermission.username,
metadata: metadataKeyValuePair
}
},
{ data: false } { data: false }
); );
const permission = createMongoAbility<ProjectPermissionSet>( const permission = createMongoAbility<ProjectPermissionSet>(

View File

@@ -117,10 +117,7 @@ const SecretConditionSchema = z
.object({ .object({
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN], [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN]
[PermissionConditionOperators.$ALL]: PermissionConditionSchema[PermissionConditionOperators.$ALL],
[PermissionConditionOperators.$REGEX]: PermissionConditionSchema[PermissionConditionOperators.$REGEX],
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
}) })
.partial() .partial()
]), ]),
@@ -131,21 +128,6 @@ const SecretConditionSchema = z
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN], [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
[PermissionConditionOperators.$ALL]: PermissionConditionSchema[PermissionConditionOperators.$ALL],
[PermissionConditionOperators.$REGEX]: PermissionConditionSchema[PermissionConditionOperators.$REGEX],
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
})
.partial()
]),
secretName: z.union([
z.string(),
z
.object({
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
[PermissionConditionOperators.$ALL]: PermissionConditionSchema[PermissionConditionOperators.$ALL],
[PermissionConditionOperators.$REGEX]: PermissionConditionSchema[PermissionConditionOperators.$REGEX],
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB] [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
}) })
.partial() .partial()

View File

@@ -23,6 +23,7 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
import { AuthTokenType } from "@app/services/auth/auth-type"; import { AuthTokenType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TokenType } from "@app/services/auth-token/auth-token-types"; import { TokenType } from "@app/services/auth-token/auth-token-types";
import { TIdentityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal"; import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
@@ -51,6 +52,8 @@ type TSamlConfigServiceFactoryDep = {
TOrgDALFactory, TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById" "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>; >;
identityMetadataDAL: Pick<TIdentityMetadataDALFactory, "delete" | "insertMany" | "transaction">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">; orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">; orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
@@ -71,7 +74,8 @@ export const samlConfigServiceFactory = ({
permissionService, permissionService,
licenseService, licenseService,
tokenService, tokenService,
smtpService smtpService,
identityMetadataDAL
}: TSamlConfigServiceFactoryDep) => { }: TSamlConfigServiceFactoryDep) => {
const createSamlCfg = async ({ const createSamlCfg = async ({
cert, cert,
@@ -332,7 +336,8 @@ export const samlConfigServiceFactory = ({
lastName, lastName,
authProvider, authProvider,
orgId, orgId,
relayState relayState,
metadata
}: TSamlLoginDTO) => { }: TSamlLoginDTO) => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = await getServerCfg(); const serverCfg = await getServerCfg();
@@ -386,6 +391,21 @@ export const samlConfigServiceFactory = ({
); );
} }
if (metadata && foundUser.id) {
await identityMetadataDAL.delete({ userId: foundUser.id, orgId }, tx);
if (metadata.length) {
await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({
userId: foundUser.id,
orgId,
key,
value
})),
tx
);
}
}
return foundUser; return foundUser;
}); });
} else { } else {
@@ -474,6 +494,20 @@ export const samlConfigServiceFactory = ({
); );
} }
if (metadata && newUser.id) {
await identityMetadataDAL.delete({ userId: newUser.id, orgId }, tx);
if (metadata.length) {
await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({
userId: newUser?.id,
orgId,
key,
value
})),
tx
);
}
}
return newUser; return newUser;
}); });
} }

View File

@@ -53,4 +53,5 @@ export type TSamlLoginDTO = {
orgId: string; orgId: string;
// saml thingy // saml thingy
relayState?: string; relayState?: string;
metadata?: { key: string; value: string }[];
}; };

View File

@@ -52,3 +52,21 @@ export const unique = <T, K extends string | number | symbol>(array: readonly T[
); );
return Object.values(valueMap); return Object.values(valueMap);
}; };
/**
* Convert an array to a dictionary by mapping each item
* into a dictionary key & value
*/
export const objectify = <T, Key extends string | number | symbol, Value = T>(
array: readonly T[],
getKey: (item: T) => Key,
getValue: (item: T) => Value = (item) => item as unknown as Value
): Record<Key, Value> => {
return array.reduce(
(acc, item) => {
acc[getKey(item)] = getValue(item);
return acc;
},
{} as Record<Key, Value>
);
};

View File

@@ -388,6 +388,7 @@ export const registerRoutes = async (
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL }); const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL });
const samlService = samlConfigServiceFactory({ const samlService = samlConfigServiceFactory({
identityMetadataDAL,
permissionService, permissionService,
orgBotDAL, orgBotDAL,
orgDAL, orgDAL,

View File

@@ -56,11 +56,11 @@ export const identityOrgDALFactory = (db: TDbClient) => {
queryBuilder.on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`); queryBuilder.on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`);
}) })
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
.leftJoin( .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
TableName.IdentityMetadata, void queryBuilder
`${TableName.IdentityMetadata}.identityOrgMembershipId`, .on(`${TableName.IdentityOrgMembership}.identityId`, `${TableName.IdentityMetadata}.identityId`)
`${TableName.IdentityOrgMembership}.id` .andOn(`${TableName.IdentityOrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
) })
.select(selectAllTableCols(TableName.IdentityOrgMembership)) .select(selectAllTableCols(TableName.IdentityOrgMembership))
// cr stands for custom role // cr stands for custom role
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) .select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))

View File

@@ -73,7 +73,7 @@ export const identityServiceFactory = ({
const identity = await identityDAL.transaction(async (tx) => { const identity = await identityDAL.transaction(async (tx) => {
const newIdentity = await identityDAL.create({ name }, tx); const newIdentity = await identityDAL.create({ name }, tx);
const identityOrgMembership = await identityOrgMembershipDAL.create( await identityOrgMembershipDAL.create(
{ {
identityId: newIdentity.id, identityId: newIdentity.id,
orgId, orgId,
@@ -85,7 +85,8 @@ export const identityServiceFactory = ({
if (metadata && metadata.length) { if (metadata && metadata.length) {
await identityMetadataDAL.insertMany( await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({ metadata.map(({ key, value }) => ({
identityOrgMembershipId: identityOrgMembership.id, identityId: newIdentity.id,
orgId,
key, key,
value value
})), })),
@@ -159,11 +160,12 @@ export const identityServiceFactory = ({
); );
} }
if (metadata) { if (metadata) {
await identityMetadataDAL.delete({ identityOrgMembershipId: identityOrgMembership.id }, tx); await identityMetadataDAL.delete({ orgId: identityOrgMembership.orgId, identityId: id }, tx);
if (metadata.length) { if (metadata.length) {
await identityMetadataDAL.insertMany( await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({ metadata.map(({ key, value }) => ({
identityOrgMembershipId: identityOrgMembership.id, identityId: newIdentity.id,
orgId: identityOrgMembership.orgId,
key, key,
value value
})), })),

View File

@@ -19,11 +19,11 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
`${TableName.UserEncryptionKey}.userId`, `${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id` `${TableName.Users}.id`
) )
.leftJoin( .leftJoin(TableName.IdentityMetadata, (queryBuilder) => {
TableName.IdentityMetadata, void queryBuilder
`${TableName.IdentityMetadata}.userOrgMembershipId`, .on(`${TableName.OrgMembership}.userId`, `${TableName.IdentityMetadata}.userId`)
`${TableName.OrgMembership}.id` .andOn(`${TableName.OrgMembership}.orgId`, `${TableName.IdentityMetadata}.orgId`);
) })
.select( .select(
db.ref("id").withSchema(TableName.OrgMembership), db.ref("id").withSchema(TableName.OrgMembership),
db.ref("inviteEmail").withSchema(TableName.OrgMembership), db.ref("inviteEmail").withSchema(TableName.OrgMembership),

View File

@@ -79,7 +79,7 @@ type TOrgServiceFactoryDep = {
"findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction" "findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction"
>; >;
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey">; projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOne">; orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOne" | "findById">;
incidentContactDAL: TIncidentContactsDALFactory; incidentContactDAL: TIncidentContactsDALFactory;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne" | "findEnforceableSamlCfg">; samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne" | "findEnforceableSamlCfg">;
smtpService: TSmtpService; smtpService: TSmtpService;
@@ -413,11 +413,10 @@ export const orgServiceFactory = ({
const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId); const { permission } = await permissionService.getUserOrgPermission(userId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member);
const foundMembership = await orgMembershipDAL.findOne({ const foundMembership = await orgMembershipDAL.findById(membershipId);
id: membershipId,
orgId
});
if (!foundMembership) throw new NotFoundError({ message: "Failed to find organization membership" }); if (!foundMembership) throw new NotFoundError({ message: "Failed to find organization membership" });
if (foundMembership.orgId !== orgId)
throw new UnauthorizedError({ message: "Updated org member doesn't belong to the organization" });
if (foundMembership.userId === userId) if (foundMembership.userId === userId)
throw new UnauthorizedError({ message: "Cannot update own organization membership" }); throw new UnauthorizedError({ message: "Cannot update own organization membership" });
@@ -444,11 +443,12 @@ export const orgServiceFactory = ({
); );
if (metadata) { if (metadata) {
await identityMetadataDAL.delete({ userOrgMembershipId: updatedOrgMembership.id }, tx); await identityMetadataDAL.delete({ userId: updatedOrgMembership.userId, orgId }, tx);
if (metadata.length) { if (metadata.length) {
await identityMetadataDAL.insertMany( await identityMetadataDAL.insertMany(
metadata.map(({ key, value }) => ({ metadata.map(({ key, value }) => ({
userOrgMembershipId: updatedOrgMembership.id, userId: updatedOrgMembership.userId,
orgId,
key, key,
value value
})), })),