Address PR comments

This commit is contained in:
Carlos Monastyrski
2025-09-30 00:29:20 -03:00
parent 9d970d3c54
commit ab2265b890
6 changed files with 80 additions and 81 deletions
@@ -5,19 +5,19 @@ import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasEnableGroupSyncCol = await knex.schema.hasColumn(TableName.SamlConfig, "enableGroupSync");
await knex.schema.alterTable(TableName.SamlConfig, (tb) => {
if (!hasEnableGroupSyncCol) {
if (!hasEnableGroupSyncCol) {
await knex.schema.alterTable(TableName.SamlConfig, (tb) => {
tb.boolean("enableGroupSync").notNullable().defaultTo(false);
}
});
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasEnableGroupSyncCol = await knex.schema.hasColumn(TableName.SamlConfig, "enableGroupSync");
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
if (hasEnableGroupSyncCol) {
if (hasEnableGroupSyncCol) {
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
t.dropColumn("enableGroupSync");
}
});
});
}
}
+2 -2
View File
@@ -327,7 +327,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
entryPoint: z.string().trim().describe(SamlSso.CREATE_CONFIG.entryPoint),
issuer: z.string().trim().describe(SamlSso.CREATE_CONFIG.issuer),
cert: z.string().trim().describe(SamlSso.CREATE_CONFIG.cert),
enableGroupSync: z.boolean().optional()
enableGroupSync: z.boolean().optional().describe(SamlSso.CREATE_CONFIG.enableGroupSync)
}),
response: {
200: SanitizedSamlConfigSchema
@@ -376,7 +376,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
entryPoint: z.string().trim().describe(SamlSso.UPDATE_CONFIG.entryPoint),
issuer: z.string().trim().describe(SamlSso.UPDATE_CONFIG.issuer),
cert: z.string().trim().describe(SamlSso.UPDATE_CONFIG.cert),
enableGroupSync: z.boolean().optional()
enableGroupSync: z.boolean().optional().describe(SamlSso.UPDATE_CONFIG.enableGroupSync)
})
.partial()
.merge(z.object({ organizationId: z.string().trim().describe(SamlSso.UPDATE_CONFIG.organizationId) })),
@@ -1,6 +1,7 @@
/* eslint-disable no-await-in-loop */
import { ForbiddenError } from "@casl/ability";
import { Knex } from "knex";
import RE2 from "re2";
import {
OrgMembershipRole,
@@ -103,6 +104,31 @@ export const samlConfigServiceFactory = ({
identityMetadataDAL,
kmsService
}: TSamlConfigServiceFactoryDep): TSamlConfigServiceFactory => {
const parseSamlGroups = (groupsValue: string): string[] => {
let samlGroups: string[] = [];
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(groupsValue);
if (Array.isArray(parsed)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
samlGroups = parsed;
} else if (typeof parsed === "string") {
samlGroups = parsed
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
} catch {
samlGroups = groupsValue
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
return samlGroups;
};
const syncUserGroupMemberships = async ({
userId,
orgId,
@@ -147,9 +173,9 @@ export const samlConfigServiceFactory = ({
const newGroup = await groupDAL.create(
{
name: groupName,
slug: `${groupName.toLowerCase().replace(/[^a-z0-9]/g, "-")}-${Date.now()}`,
slug: `${groupName.toLowerCase().replace(new RE2("[^a-z0-9]", "g"), "-")}-${Date.now()}`,
orgId,
role: OrgMembershipRole.Member,
role: OrgMembershipRole.NoAccess,
roleId: null
},
transaction
@@ -249,7 +275,7 @@ export const samlConfigServiceFactory = ({
if (enableGroupSync && !GROUP_SYNC_SUPPORTED_PROVIDERS.includes(authProvider)) {
throw new BadRequestError({
message: "Group sync is only supported for Google SAML SSO."
message: "Group sync is not supported for this SAML provider."
});
}
@@ -458,7 +484,7 @@ export const samlConfigServiceFactory = ({
const samlConfig = await samlConfigDAL.findOne({ orgId });
const groupsMetadata = metadata?.find(({ key }) => key === "groups");
const shouldSyncGroups = !!(samlConfig?.enableGroupSync && groupsMetadata?.value);
const shouldSyncGroups = !!samlConfig?.enableGroupSync;
let user: TUsers;
if (userAlias) {
@@ -481,7 +507,7 @@ export const samlConfigServiceFactory = ({
orgId,
role,
roleId,
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited,
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited,
isActive: true
},
tx
@@ -512,36 +538,15 @@ export const samlConfigServiceFactory = ({
}
}
if (shouldSyncGroups && metadata && foundUser.id && groupsMetadata?.value) {
let samlGroups: string[] = [];
if (shouldSyncGroups && metadata && foundUser.id) {
const samlGroups = groupsMetadata?.value ? parseSamlGroups(groupsMetadata.value) : [];
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(groupsMetadata.value);
if (Array.isArray(parsed)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
samlGroups = parsed;
} else if (typeof parsed === "string") {
samlGroups = parsed
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
} catch {
samlGroups = groupsMetadata.value
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
if (samlGroups.length > 0) {
await syncUserGroupMemberships({
userId: foundUser.id,
orgId,
samlGroups,
tx
});
}
await syncUserGroupMemberships({
userId: foundUser.id,
orgId,
samlGroups,
tx
});
}
return foundUser;
@@ -605,11 +610,12 @@ export const samlConfigServiceFactory = ({
orgId,
role,
roleId,
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited,
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited, // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
isActive: true
},
tx
);
// Only update the membership to Accepted if the user account is already completed.
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
await orgDAL.updateMembershipById(
orgMembership.id,
@@ -635,36 +641,15 @@ export const samlConfigServiceFactory = ({
}
}
if (shouldSyncGroups && metadata && newUser.id && groupsMetadata?.value) {
let samlGroups: string[] = [];
if (shouldSyncGroups && metadata && newUser.id) {
const samlGroups = groupsMetadata?.value ? parseSamlGroups(groupsMetadata.value) : [];
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(groupsMetadata.value);
if (Array.isArray(parsed)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
samlGroups = parsed;
} else if (typeof parsed === "string") {
samlGroups = parsed
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
} catch {
samlGroups = groupsMetadata.value
.split(",")
.map((g) => g.trim())
.filter(Boolean);
}
if (samlGroups.length > 0) {
await syncUserGroupMemberships({
userId: newUser.id,
orgId,
samlGroups,
tx
});
}
await syncUserGroupMemberships({
userId: newUser.id,
orgId,
samlGroups,
tx
});
}
return newUser;
+6 -2
View File
@@ -2872,7 +2872,9 @@ export const SamlSso = {
entryPoint:
"The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.",
issuer: "The SAML provider issuer URL or entity ID.",
cert: "The certificate to use for SAML authentication."
cert: "The certificate to use for SAML authentication.",
enableGroupSync:
"Whether to enable automatic synchronization of group memberships from the SAML provider to Infisical groups."
},
CREATE_CONFIG: {
organizationId: "The ID of the organization to create the SAML config for.",
@@ -2881,7 +2883,9 @@ export const SamlSso = {
entryPoint:
"The entry point for the SAML authentication. This is the URL that the user will be redirected to after they have authenticated with the SAML provider.",
issuer: "The SAML provider issuer URL or entity ID.",
cert: "The certificate to use for SAML authentication."
cert: "The certificate to use for SAML authentication.",
enableGroupSync:
"Whether to enable automatic synchronization of group memberships from the SAML provider to Infisical groups."
}
};