Compare commits

...

16 Commits

Author SHA1 Message Date
1aa9be203e improvement: move sso/provision settings back to org settings tabs 2025-07-11 10:58:35 -07:00
e35ac599f8 Merge pull request #3997 from Infisical/fix-approval-requests-blocking-deletion
fix(approval-workflows): allow null committer on secret approval request and cascade delete on access request
2025-07-11 10:05:19 -07:00
6d91297ca9 Merge pull request #4005 from Infisical/fix/billingPageIdentityLimit
fix(billing): fix feature flags to only use identityLimit
2025-07-11 12:14:58 -03:00
db369b8f51 fix(billing): fix feature flags to only use identityLimit and minor fix invalidate plan query result 2025-07-11 11:36:25 -03:00
a50a95ad6e Merge pull request #3923 from Infisical/daniel/approval-policy-improvements
fix(approval-policies): improve policies handling
2025-07-11 11:44:09 +04:00
4ec0031c42 Merge pull request #4003 from Infisical/offline-docs-dockerfile-update
Allow docs to run fully offline
2025-07-10 21:22:40 -04:00
a6edb67f58 Allow docs to run fully offline 2025-07-10 20:34:56 -04:00
aae5831f35 Merge pull request #4001 from Infisical/server-admin-sidebar-improvements
improvement(frontend): Server admin sidebar improvements
2025-07-10 15:44:25 -07:00
6f78a6b4c1 Merge pull request #4000 from Infisical/fix-remove-jim-as-sole-author-of-secret-leaks
fix(secret-scanning-v2): Remove Jim as sole author of all secret leaks
2025-07-10 15:41:24 -07:00
c2e326b95a fix: remove jim as sole author of all secret leaks 2025-07-10 15:02:38 -07:00
97c96acea5 Update secret-approval-policy-service.ts 2025-07-11 00:59:28 +04:00
5e24015f2a requested changes 2025-07-11 00:54:28 +04:00
f17e1f6699 fix: update approval request user delettion behavior 2025-07-10 10:37:37 -07:00
e71b136859 requested changes 2025-07-10 16:14:40 +04:00
7d2d69fc7d requested changes 2025-07-05 01:56:35 +04:00
0569c7e692 fix(approval-policies): improve policies handling 2025-07-04 03:14:43 +04:00
65 changed files with 375 additions and 286 deletions

View File

@ -0,0 +1,55 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const existingSecretApprovalPolicies = await knex(TableName.SecretApprovalPolicy)
.whereNull("secretPath")
.orWhere("secretPath", "");
const existingAccessApprovalPolicies = await knex(TableName.AccessApprovalPolicy)
.whereNull("secretPath")
.orWhere("secretPath", "");
// update all the secret approval policies secretPath to be "/**"
if (existingSecretApprovalPolicies.length) {
await knex(TableName.SecretApprovalPolicy)
.whereIn(
"id",
existingSecretApprovalPolicies.map((el) => el.id)
)
.update({
secretPath: "/**"
});
}
// update all the access approval policies secretPath to be "/**"
if (existingAccessApprovalPolicies.length) {
await knex(TableName.AccessApprovalPolicy)
.whereIn(
"id",
existingAccessApprovalPolicies.map((el) => el.id)
)
.update({
secretPath: "/**"
});
}
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (table) => {
table.string("secretPath").notNullable().alter();
});
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (table) => {
table.string("secretPath").notNullable().alter();
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (table) => {
table.string("secretPath").nullable().alter();
});
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (table) => {
table.string("secretPath").nullable().alter();
});
}

View File

@ -0,0 +1,35 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
const hasCommitterCol = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId");
if (hasCommitterCol) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
tb.uuid("committerUserId").nullable().alter();
});
}
const hasRequesterCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedByUserId");
if (hasRequesterCol) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
tb.dropForeign("requestedByUserId");
tb.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
// can't undo committer nullable
const hasRequesterCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedByUserId");
if (hasRequesterCol) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
tb.dropForeign("requestedByUserId");
tb.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
});
}
}

View File

@ -14,8 +14,8 @@ export const AccessApprovalPoliciesApproversSchema = z.object({
updatedAt: z.date(),
approverUserId: z.string().uuid().nullable().optional(),
approverGroupId: z.string().uuid().nullable().optional(),
sequence: z.number().default(0).nullable().optional(),
approvalsRequired: z.number().default(1).nullable().optional()
sequence: z.number().default(1).nullable().optional(),
approvalsRequired: z.number().nullable().optional()
});
export type TAccessApprovalPoliciesApprovers = z.infer<typeof AccessApprovalPoliciesApproversSchema>;

View File

@ -11,7 +11,7 @@ export const AccessApprovalPoliciesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
approvals: z.number().default(1),
secretPath: z.string().nullable().optional(),
secretPath: z.string(),
envId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),

View File

@ -12,8 +12,8 @@ export const CertificateAuthoritiesSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string(),
enableDirectIssuance: z.boolean().default(true),
status: z.string(),
enableDirectIssuance: z.boolean().default(true),
name: z.string()
});

View File

@ -25,8 +25,8 @@ export const CertificatesSchema = z.object({
certificateTemplateId: z.string().uuid().nullable().optional(),
keyUsages: z.string().array().nullable().optional(),
extendedKeyUsages: z.string().array().nullable().optional(),
pkiSubscriberId: z.string().uuid().nullable().optional(),
projectId: z.string()
projectId: z.string(),
pkiSubscriberId: z.string().uuid().nullable().optional()
});
export type TCertificates = z.infer<typeof CertificatesSchema>;

View File

@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
export const SecretApprovalPoliciesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
secretPath: z.string().nullable().optional(),
secretPath: z.string(),
approvals: z.number().default(1),
envId: z.string().uuid(),
createdAt: z.date(),

View File

@ -18,7 +18,7 @@ export const SecretApprovalRequestsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
isReplicated: z.boolean().nullable().optional(),
committerUserId: z.string().uuid(),
committerUserId: z.string().uuid().nullable().optional(),
statusChangedByUserId: z.string().uuid().nullable().optional(),
bypassReason: z.string().nullable().optional()
});

View File

@ -2,6 +2,7 @@ import { nanoid } from "nanoid";
import { z } from "zod";
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { EnforcementLevel } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -19,7 +20,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
body: z.object({
projectSlug: z.string().trim(),
name: z.string().optional(),
secretPath: z.string().trim().default("/"),
secretPath: z.string().trim().min(1, { message: "Secret path cannot be empty" }).transform(removeTrailingSlash),
environment: z.string(),
approvers: z
.discriminatedUnion("type", [
@ -174,8 +175,9 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
secretPath: z
.string()
.trim()
.min(1, { message: "Secret path cannot be empty" })
.optional()
.transform((val) => (val === "" ? "/" : val)),
.transform((val) => (val ? removeTrailingSlash(val) : val)),
approvers: z
.discriminatedUnion("type", [
z.object({

View File

@ -23,10 +23,8 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
environment: z.string(),
secretPath: z
.string()
.optional()
.nullable()
.default("/")
.transform((val) => (val ? removeTrailingSlash(val) : val)),
.min(1, { message: "Secret path cannot be empty" })
.transform((val) => removeTrailingSlash(val)),
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
@ -100,10 +98,10 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
approvals: z.number().min(1).default(1),
secretPath: z
.string()
.trim()
.min(1, { message: "Secret path cannot be empty" })
.optional()
.nullable()
.transform((val) => (val ? removeTrailingSlash(val) : val))
.transform((val) => (val === "" ? "/" : val)),
.transform((val) => (val ? removeTrailingSlash(val) : undefined)),
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
allowedSelfApprovals: z.boolean().default(true)
}),

View File

@ -58,7 +58,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
deletedAt: z.date().nullish(),
allowedSelfApprovals: z.boolean()
}),
committerUser: approvalRequestUser,
committerUser: approvalRequestUser.nullish(),
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
environment: z.string(),
reviewers: z.object({ userId: z.string(), status: z.string() }).array(),
@ -308,7 +308,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
}),
environment: z.string(),
statusChangedByUser: approvalRequestUser.optional(),
committerUser: approvalRequestUser,
committerUser: approvalRequestUser.nullish(),
reviewers: approvalRequestUser.extend({ status: z.string(), comment: z.string().optional() }).array(),
secretPath: z.string(),
commits: secretRawSchema

View File

@ -53,7 +53,7 @@ export interface TAccessApprovalPolicyDALFactory
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -93,7 +93,7 @@ export interface TAccessApprovalPolicyDALFactory
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -116,7 +116,7 @@ export interface TAccessApprovalPolicyDALFactory
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
}>;
findLastValidPolicy: (
@ -138,7 +138,7 @@ export interface TAccessApprovalPolicyDALFactory
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
}
| undefined
@ -190,7 +190,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
}>;
deleteAccessApprovalPolicy: ({
@ -214,7 +214,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -252,7 +252,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
}>;
getAccessApprovalPolicyByProjectSlug: ({
@ -286,7 +286,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -337,7 +337,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;

View File

@ -60,6 +60,26 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalRequestReviewerDAL,
orgMembershipDAL
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
const $policyExists = async ({
envId,
secretPath,
policyId
}: {
envId: string;
secretPath: string;
policyId?: string;
}) => {
const policy = await accessApprovalPolicyDAL
.findOne({
envId,
secretPath,
deletedAt: null
})
.catch(() => null);
return policyId ? policy && policy.id !== policyId : Boolean(policy);
};
const createAccessApprovalPolicy: TAccessApprovalPolicyServiceFactory["createAccessApprovalPolicy"] = async ({
name,
actor,
@ -106,6 +126,12 @@ export const accessApprovalPolicyServiceFactory = ({
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
if (!env) throw new NotFoundError({ message: `Environment with slug '${environment}' not found` });
if (await $policyExists({ envId: env.id, secretPath })) {
throw new BadRequestError({
message: `A policy for secret path '${secretPath}' already exists in environment '${environment}'`
});
}
let approverUserIds = userApprovers;
if (userApproverNames.length) {
const approverUsersInDB = await userDAL.find({
@ -279,7 +305,11 @@ export const accessApprovalPolicyServiceFactory = ({
) as { username: string; sequence?: number }[];
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
if (!accessApprovalPolicy) throw new BadRequestError({ message: "Approval policy not found" });
if (!accessApprovalPolicy) {
throw new NotFoundError({
message: `Access approval policy with ID '${policyId}' not found`
});
}
const currentApprovals = approvals || accessApprovalPolicy.approvals;
if (
@ -290,9 +320,18 @@ export const accessApprovalPolicyServiceFactory = ({
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
}
if (!accessApprovalPolicy) {
throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` });
if (
await $policyExists({
envId: accessApprovalPolicy.envId,
secretPath: secretPath || accessApprovalPolicy.secretPath,
policyId: accessApprovalPolicy.id
})
) {
throw new BadRequestError({
message: `A policy for secret path '${secretPath}' already exists in environment '${accessApprovalPolicy.environment.slug}'`
});
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,

View File

@ -122,7 +122,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
}>;
deleteAccessApprovalPolicy: ({
@ -146,7 +146,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -218,7 +218,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;
@ -269,7 +269,7 @@ export interface TAccessApprovalPolicyServiceFactory {
envId: string;
enforcementLevel: string;
allowedSelfApprovals: boolean;
secretPath?: string | null | undefined;
secretPath: string;
deletedAt?: Date | null | undefined;
environment: {
id: string;

View File

@ -1711,7 +1711,7 @@ interface SecretApprovalReopened {
interface SecretApprovalRequest {
type: EventType.SECRET_APPROVAL_REQUEST;
metadata: {
committedBy: string;
committedBy?: string | null;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
eventType: SecretApprovalEvent;

View File

@ -361,13 +361,6 @@ export const ldapConfigServiceFactory = ({
});
} else {
const plan = await licenseService.getPlan(orgId);
if (plan?.slug !== "enterprise" && plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
throw new BadRequestError({
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
});
}
if (plan?.slug !== "enterprise" && plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({

View File

@ -1,5 +1,4 @@
export const BillingPlanRows = {
MemberLimit: { name: "Organization member limit", field: "memberLimit" },
IdentityLimit: { name: "Organization identity limit", field: "identityLimit" },
WorkspaceLimit: { name: "Project limit", field: "workspaceLimit" },
EnvironmentLimit: { name: "Environment limit", field: "environmentLimit" },

View File

@ -442,9 +442,7 @@ export const licenseServiceFactory = ({
rows: data.rows.map((el) => {
let used = "-";
if (el.name === BillingPlanRows.MemberLimit.name) {
used = orgMembersUsed.toString();
} else if (el.name === BillingPlanRows.WorkspaceLimit.name) {
if (el.name === BillingPlanRows.WorkspaceLimit.name) {
used = projectCount.toString();
} else if (el.name === BillingPlanRows.IdentityLimit.name) {
used = (identityUsed + orgMembersUsed).toString();
@ -464,12 +462,10 @@ export const licenseServiceFactory = ({
const allowed = onPremFeatures[field as keyof TFeatureSet];
let used = "-";
if (field === BillingPlanRows.MemberLimit.field) {
used = orgMembersUsed.toString();
} else if (field === BillingPlanRows.WorkspaceLimit.field) {
if (field === BillingPlanRows.WorkspaceLimit.field) {
used = projectCount.toString();
} else if (field === BillingPlanRows.IdentityLimit.field) {
used = identityUsed.toString();
used = (identityUsed + orgMembersUsed).toString();
}
return {

View File

@ -311,13 +311,6 @@ export const samlConfigServiceFactory = ({
});
} else {
const plan = await licenseService.getPlan(orgId);
if (plan?.slug !== "enterprise" && plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
throw new BadRequestError({
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
});
}
if (plan?.slug !== "enterprise" && plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({

View File

@ -55,6 +55,26 @@ export const secretApprovalPolicyServiceFactory = ({
licenseService,
secretApprovalRequestDAL
}: TSecretApprovalPolicyServiceFactoryDep) => {
const $policyExists = async ({
envId,
secretPath,
policyId
}: {
envId: string;
secretPath: string;
policyId?: string;
}) => {
const policy = await secretApprovalPolicyDAL
.findOne({
envId,
secretPath,
deletedAt: null
})
.catch(() => null);
return policyId ? policy && policy.id !== policyId : Boolean(policy);
};
const createSecretApprovalPolicy = async ({
name,
actor,
@ -106,10 +126,17 @@ export const secretApprovalPolicyServiceFactory = ({
}
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
if (!env)
if (!env) {
throw new NotFoundError({
message: `Environment with slug '${environment}' not found in project with ID ${projectId}`
});
}
if (await $policyExists({ envId: env.id, secretPath })) {
throw new BadRequestError({
message: `A policy for secret path '${secretPath}' already exists in environment '${environment}'`
});
}
let groupBypassers: string[] = [];
let bypasserUserIds: string[] = [];
@ -260,6 +287,18 @@ export const secretApprovalPolicyServiceFactory = ({
});
}
if (
await $policyExists({
envId: secretApprovalPolicy.envId,
secretPath: secretPath || secretApprovalPolicy.secretPath,
policyId: secretApprovalPolicy.id
})
) {
throw new BadRequestError({
message: `A policy for secret path '${secretPath}' already exists in environment '${secretApprovalPolicy.environment.slug}'`
});
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,

View File

@ -4,7 +4,7 @@ import { ApproverType, BypasserType } from "../access-approval-policy/access-app
export type TCreateSapDTO = {
approvals: number;
secretPath?: string | null;
secretPath: string;
environment: string;
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
bypassers?: (
@ -20,7 +20,7 @@ export type TCreateSapDTO = {
export type TUpdateSapDTO = {
secretPolicyId: string;
approvals?: number;
secretPath?: string | null;
secretPath?: string;
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
bypassers?: (
| { type: BypasserType.Group; id: string }

View File

@ -45,7 +45,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalRequest}.statusChangedByUserId`,
`statusChangedByUser.id`
)
.join<TUsers>(
.leftJoin<TUsers>(
db(TableName.Users).as("committerUser"),
`${TableName.SecretApprovalRequest}.committerUserId`,
`committerUser.id`
@ -173,13 +173,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
username: el.statusChangedByUserUsername
}
: undefined,
committerUser: {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
},
committerUser: el.committerUserId
? {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
}
: null,
policy: {
id: el.policyId,
name: el.policyName,
@ -377,7 +379,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalPolicyBypasser}.bypasserGroupId`,
`bypasserUserGroupMembership.groupId`
)
.join<TUsers>(
.leftJoin<TUsers>(
db(TableName.Users).as("committerUser"),
`${TableName.SecretApprovalRequest}.committerUserId`,
`committerUser.id`
@ -488,13 +490,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
enforcementLevel: el.policyEnforcementLevel,
allowedSelfApprovals: el.policyAllowedSelfApprovals
},
committerUser: {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
}
committerUser: el.committerUserId
? {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
}
: null
}),
childrenMapper: [
{
@ -581,7 +585,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalPolicyBypasser}.bypasserGroupId`,
`bypasserUserGroupMembership.groupId`
)
.join<TUsers>(
.leftJoin<TUsers>(
db(TableName.Users).as("committerUser"),
`${TableName.SecretApprovalRequest}.committerUserId`,
`committerUser.id`
@ -693,13 +697,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
enforcementLevel: el.policyEnforcementLevel,
allowedSelfApprovals: el.policyAllowedSelfApprovals
},
committerUser: {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
}
committerUser: el.committerUserId
? {
userId: el.committerUserId,
email: el.committerUserEmail,
firstName: el.committerUserFirstName,
lastName: el.committerUserLastName,
username: el.committerUserUsername
}
: null
}),
childrenMapper: [
{

View File

@ -1320,7 +1320,7 @@ export const secretApprovalRequestServiceFactory = ({
});
const env = await projectEnvDAL.findOne({ id: policy.envId });
const user = await userDAL.findById(secretApprovalRequest.committerUserId);
const user = await userDAL.findById(actorId);
await triggerWorkflowIntegrationNotification({
input: {
@ -1657,7 +1657,7 @@ export const secretApprovalRequestServiceFactory = ({
return { ...doc, commits: approvalCommits };
});
const user = await userDAL.findById(secretApprovalRequest.committerUserId);
const user = await userDAL.findById(actorId);
const env = await projectEnvDAL.findOne({ id: policy.envId });
await triggerWorkflowIntegrationNotification({

View File

@ -37,7 +37,8 @@ import {
TQueueSecretScanningDataSourceFullScan,
TQueueSecretScanningResourceDiffScan,
TQueueSecretScanningSendNotification,
TSecretScanningDataSourceWithConnection
TSecretScanningDataSourceWithConnection,
TSecretScanningFinding
} from "./secret-scanning-v2-types";
type TSecretRotationV2QueueServiceFactoryDep = {
@ -459,13 +460,16 @@ export const secretScanningV2QueueServiceFactory = async ({
const newFindings = allFindings.filter((finding) => finding.scanId === scanId);
if (newFindings.length) {
const finding = newFindings[0] as TSecretScanningFinding;
await queueService.queuePg(QueueJobs.SecretScanningV2SendNotification, {
status: SecretScanningScanStatus.Completed,
resourceName: resource.name,
isDiffScan: true,
dataSource,
numberOfSecrets: newFindings.length,
scanId
scanId,
authorName: finding?.details?.author,
authorEmail: finding?.details?.email
});
}
@ -582,8 +586,8 @@ export const secretScanningV2QueueServiceFactory = async ({
substitutions:
payload.status === SecretScanningScanStatus.Completed
? {
authorName: "Jim",
authorEmail: "jim@infisical.com",
authorName: payload.authorName,
authorEmail: payload.authorEmail,
resourceName,
numberOfSecrets: payload.numberOfSecrets,
isDiffScan: payload.isDiffScan,

View File

@ -119,7 +119,14 @@ export type TQueueSecretScanningSendNotification = {
resourceName: string;
} & (
| { status: SecretScanningScanStatus.Failed; errorMessage: string }
| { status: SecretScanningScanStatus.Completed; numberOfSecrets: number; scanId: string; isDiffScan: boolean }
| {
status: SecretScanningScanStatus.Completed;
numberOfSecrets: number;
scanId: string;
isDiffScan: boolean;
authorName?: string;
authorEmail?: string;
}
);
export type TCloneRepository = {

View File

@ -912,14 +912,6 @@ export const orgServiceFactory = ({
// if there exist no org membership we set is as given by the request
if (!inviteeOrgMembership) {
if (plan?.slug !== "enterprise" && plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
throw new BadRequestError({
name: "InviteUser",
message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members."
});
}
if (plan?.slug !== "enterprise" && plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({

View File

@ -1,6 +1,32 @@
FROM node:20-alpine
FROM node:20-alpine AS builder
WORKDIR /app
RUN npm install -g mint
RUN npm install -g mint@4.2.13
COPY . .
# Install a local version of our OpenAPI spec
RUN apk add --no-cache wget jq && \
wget -O spec.json https://app.infisical.com/api/docs/json && \
jq '.api.openapi = "./spec.json"' docs.json > temp.json && \
mv temp.json docs.json
# Run mint dev briefly to download the web client
RUN timeout 30 mint dev || true
FROM node:20-alpine
WORKDIR /app
RUN npm install -g mint@4.2.13
COPY . .
COPY --from=builder /root/.mintlify /root/.mintlify
COPY --from=builder /app/docs.json /app/docs.json
COPY --from=builder /app/spec.json /app/spec.json
EXPOSE 3000
CMD ["mint", "dev"]

View File

@ -78,7 +78,10 @@
},
{
"group": "Infisical SSH",
"pages": ["documentation/platform/ssh/overview", "documentation/platform/ssh/host-groups"]
"pages": [
"documentation/platform/ssh/overview",
"documentation/platform/ssh/host-groups"
]
},
{
"group": "Key Management (KMS)",
@ -375,7 +378,10 @@
},
{
"group": "Architecture",
"pages": ["internals/architecture/components", "internals/architecture/cloud"]
"pages": [
"internals/architecture/components",
"internals/architecture/cloud"
]
},
"internals/security",
"internals/service-tokens"
@ -546,7 +552,10 @@
"integrations/cloud/gcp-secret-manager",
{
"group": "Cloudflare",
"pages": ["integrations/cloud/cloudflare-pages", "integrations/cloud/cloudflare-workers"]
"pages": [
"integrations/cloud/cloudflare-pages",
"integrations/cloud/cloudflare-workers"
]
},
"integrations/cloud/terraform-cloud",
"integrations/cloud/databricks",
@ -658,7 +667,11 @@
"cli/commands/reset",
{
"group": "infisical scan",
"pages": ["cli/commands/scan", "cli/commands/scan-git-changes", "cli/commands/scan-install"]
"pages": [
"cli/commands/scan",
"cli/commands/scan-git-changes",
"cli/commands/scan-install"
]
}
]
},
@ -982,7 +995,9 @@
"pages": [
{
"group": "Kubernetes",
"pages": ["api-reference/endpoints/dynamic-secrets/kubernetes/create-lease"]
"pages": [
"api-reference/endpoints/dynamic-secrets/kubernetes/create-lease"
]
},
"api-reference/endpoints/dynamic-secrets/create",
"api-reference/endpoints/dynamic-secrets/update",

View File

@ -29,10 +29,6 @@ export const ROUTE_PATHS = Object.freeze({
"/_authenticate/_inject-org-details/_org-layout/organization/settings/oauth/callback"
)
},
SsoPage: setRoute(
"/organization/sso",
"/_authenticate/_inject-org-details/_org-layout/organization/sso"
),
SecretSharing: setRoute(
"/organization/secret-sharing",
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/"

View File

@ -170,7 +170,7 @@ export type TCreateAccessPolicyDTO = {
approvers?: Approver[];
bypassers?: Bypasser[];
approvals?: number;
secretPath?: string;
secretPath: string;
enforcementLevel?: EnforcementLevel;
allowedSelfApprovals: boolean;
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];

View File

@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries";
import { subscriptionQueryKeys } from "../subscriptions/queries";
import { identitiesKeys } from "./queries";
import {
AddIdentityAliCloudAuthDTO,
@ -82,6 +83,9 @@ export const useCreateIdentity = () => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
}
});
};
@ -123,6 +127,9 @@ export const useDeleteIdentity = () => {
queryClient.invalidateQueries({
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
});
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
}
});
};

View File

@ -49,7 +49,7 @@ export type TCreateSecretPolicyDTO = {
workspaceId: string;
name?: string;
environment: string;
secretPath?: string | null;
secretPath: string;
approvers?: Approver[];
bypassers?: Bypasser[];
approvals?: number;
@ -62,7 +62,7 @@ export type TUpdateSecretPolicyDTO = {
name?: string;
approvers?: Approver[];
bypassers?: Bypasser[];
secretPath?: string | null;
secretPath?: string;
approvals?: number;
allowedSelfApprovals?: boolean;
enforcementLevel?: EnforcementLevel;

View File

@ -9,6 +9,7 @@ import { APIKeyDataV2 } from "../apiKeys/types";
import { MfaMethod } from "../auth/types";
import { TGroupWithProjectMemberships } from "../groups/types";
import { setAuthToken } from "../reactQuery";
import { subscriptionQueryKeys } from "../subscriptions/queries";
import { workspaceKeys } from "../workspace";
import { userKeys } from "./query-keys";
import {
@ -188,6 +189,9 @@ export const useAddUsersToOrg = () => {
},
onSuccess: (_, { organizationId, projects }) => {
queryClient.invalidateQueries({ queryKey: userKeys.getOrgUsers(organizationId) });
queryClient.invalidateQueries({
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
});
projects?.forEach((project) => {
if (project.slug) {

View File

@ -1,6 +1,5 @@
import {
faBook,
faCheckCircle,
faCog,
faCubes,
faDoorClosed,
@ -100,18 +99,6 @@ export const OrgSidebar = ({ isHidden }: Props) => {
</MenuItem>
)}
</Link>
<Link to="/organization/sso">
{({ isActive }) => (
<MenuItem isSelected={isActive}>
<div className="mx-1 flex gap-2">
<div className="w-6">
<FontAwesomeIcon icon={faCheckCircle} className="mr-4" />
</div>
SSO Settings
</div>
</MenuItem>
)}
</Link>
<Link to="/organization/settings">
{({ isActive }) => (
<MenuItem isSelected={isActive}>

View File

@ -39,10 +39,6 @@ export const OrgMembersSection = () => {
const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership();
const { mutateAsync: updateOrgMembership } = useUpdateOrgMembership();
const isMoreUsersAllowed = subscription?.memberLimit
? subscription.membersUsed < subscription.memberLimit
: true;
const isMoreIdentitiesAllowed = subscription?.identityLimit
? subscription.identitiesUsed < subscription.identityLimit
: true;
@ -58,7 +54,7 @@ export const OrgMembersSection = () => {
return;
}
if ((!isMoreUsersAllowed || !isMoreIdentitiesAllowed) && !isEnterprise) {
if (!isMoreIdentitiesAllowed && !isEnterprise) {
handlePopUpOpen("upgradePlan", {
description: "You can add more members if you upgrade your Infisical plan."
});

View File

@ -1,6 +1,3 @@
import { Link } from "@tanstack/react-router";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { withPermission } from "@app/hoc";
@ -11,21 +8,6 @@ export const OrgSecurityTab = withPermission(
() => {
return (
<>
<NoticeBannerV2
className="mx-auto mb-4"
titleClassName="text-base"
title="Single Sign-On (SSO) Settings"
>
<p className="mt-1 text-mineshaft-300">
SSO Settings have been relocated:{" "}
<Link
className="text-mineshaft-200 underline underline-offset-2"
to="/organization/sso"
>
Click here to view SSO Settings
</Link>
</p>
</NoticeBannerV2>
<OrgGenericAuthSection />
<OrgUserAccessTokenLimitSection />
</>

View File

@ -9,8 +9,10 @@ import { ImportTab } from "../ImportTab";
import { KmipTab } from "../KmipTab/OrgKmipTab";
import { OrgEncryptionTab } from "../OrgEncryptionTab";
import { OrgGeneralTab } from "../OrgGeneralTab";
import { OrgProvisioningTab } from "../OrgProvisioningTab";
import { OrgSecurityTab } from "../OrgSecurityTab";
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab/OrgWorkflowIntegrationTab";
import { OrgSsoTab } from "../OrgSsoTab";
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab";
import { ProjectTemplatesTab } from "../ProjectTemplatesTab";
export const OrgTabGroup = () => {
@ -19,6 +21,16 @@ export const OrgTabGroup = () => {
});
const tabs = [
{ name: "General", key: "tab-org-general", component: OrgGeneralTab },
{
name: "SSO",
key: "sso-settings",
component: OrgSsoTab
},
{
name: "Provisioning",
key: "provisioning-settings",
component: OrgProvisioningTab
},
{ name: "Security", key: "tab-org-security", component: OrgSecurityTab },
{ name: "Encryption", key: "tab-org-encryption", component: OrgEncryptionTab },
{

View File

@ -0,0 +1 @@
export * from "./OrgWorkflowIntegrationTab";

View File

@ -1,21 +0,0 @@
import { Helmet } from "react-helmet";
import { PageHeader } from "@app/components/v2";
import { SsoTabGroup } from "./components/SsoTabGroup";
export const SsoPage = () => {
return (
<>
<Helmet>
<title>Single Sign-On (SSO)</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader title="Single Sign-On (SSO)" />
<SsoTabGroup />
</div>
</div>
</>
);
};

View File

@ -1,37 +0,0 @@
import { useState } from "react";
import { useSearch } from "@tanstack/react-router";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { OrgProvisioningTab } from "../OrgProvisioningTab";
import { OrgSsoTab } from "../OrgSsoTab";
export const SsoTabGroup = () => {
const search = useSearch({
from: ROUTE_PATHS.Organization.SsoPage.id
});
const tabs = [
{ name: "General", key: "tab-sso-auth", component: OrgSsoTab },
{ name: "Provisioning", key: "tab-sso-identity", component: OrgProvisioningTab }
];
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
return (
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
<TabList>
{tabs.map((tab) => (
<Tab value={tab.key} key={tab.key}>
{tab.name}
</Tab>
))}
</TabList>
{tabs.map(({ key, component: Component }) => (
<TabPanel value={key} key={`tab-panel-${key}`}>
<Component />
</TabPanel>
))}
</Tabs>
);
};

View File

@ -1 +0,0 @@
export { SsoTabGroup } from "./SsoTabGroup";

View File

@ -1,26 +0,0 @@
import { createFileRoute, stripSearchParams } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SsoPage } from "./SsoPage";
const SettingsPageQueryParams = z.object({
selectedTab: z.string().catch("")
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/sso"
)({
component: SsoPage,
validateSearch: zodValidator(SettingsPageQueryParams),
search: {
middlewares: [stripSearchParams({ selectedTab: "" })]
},
context: () => ({
breadcrumbs: [
{
label: "Single Sign-On (SSO)"
}
]
})
});

View File

@ -55,7 +55,7 @@ const formSchema = z
.object({
environment: z.object({ slug: z.string(), name: z.string() }),
name: z.string().optional(),
secretPath: z.string().optional(),
secretPath: z.string().trim().min(1),
approvals: z.number().min(1).default(1),
userApprovers: z
.object({ type: z.literal(ApproverType.User), id: z.string() })
@ -93,20 +93,19 @@ const formSchema = z
.optional()
})
.superRefine((data, ctx) => {
if (
data.policyType === PolicyType.ChangePolicy &&
!(data.groupApprovers.length || data.userApprovers.length)
) {
ctx.addIssue({
path: ["userApprovers"],
code: z.ZodIssueCode.custom,
message: "At least one approver should be provided"
});
ctx.addIssue({
path: ["groupApprovers"],
code: z.ZodIssueCode.custom,
message: "At least one approver should be provided"
});
if (data.policyType === PolicyType.ChangePolicy) {
if (!(data.groupApprovers.length || data.userApprovers.length)) {
ctx.addIssue({
path: ["userApprovers"],
code: z.ZodIssueCode.custom,
message: "At least one approver should be provided"
});
ctx.addIssue({
path: ["groupApprovers"],
code: z.ZodIssueCode.custom,
message: "At least one approver should be provided"
});
}
}
});
@ -127,6 +126,7 @@ const Form = ({
control,
handleSubmit,
watch,
resetField,
formState: { isSubmitting }
} = useForm<TFormSchema>({
resolver: zodResolver(formSchema),
@ -177,6 +177,7 @@ const Form = ({
: undefined,
defaultValues: !editValues
? {
secretPath: "/",
sequenceApprovers: [{ approvals: 1 }]
}
: undefined
@ -405,7 +406,10 @@ const Form = ({
<Select
isDisabled={isEditMode}
value={value}
onValueChange={(val) => onChange(val as PolicyType)}
onValueChange={(val) => {
onChange(val as PolicyType);
resetField("secretPath");
}}
className="w-full border border-mineshaft-500"
>
{Object.values(PolicyType).map((policyType) => {
@ -465,6 +469,7 @@ const Form = ({
<FormControl
tooltipText="Secret paths support glob patterns. For example, '/**' will match all paths."
label="Secret Path"
isRequired
isError={Boolean(error)}
errorText={error?.message}
className="flex-1"

View File

@ -338,8 +338,14 @@ export const SecretApprovalRequest = () => {
</div>
<span className="text-xs leading-3 text-gray-500">
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
{committerUser?.firstName || ""} {committerUser?.lastName || ""} (
{committerUser?.email})
{committerUser ? (
<>
{committerUser?.firstName || ""} {committerUser?.lastName || ""} (
{committerUser?.email})
</>
) : (
<span className="text-gray-600">Deleted User</span>
)}
{!isReviewed && status === "open" && " - Review required"}
</span>
</div>

View File

@ -250,10 +250,17 @@ export const SecretApprovalRequestChanges = ({
secretApprovalRequestDetails.isReplicated
)}
</div>
<span className="-mt-1 flex items-center space-x-2 text-xs text-gray-400">
By {secretApprovalRequestDetails?.committerUser?.firstName} (
{secretApprovalRequestDetails?.committerUser?.email})
</span>
<p className="-mt-1 text-xs text-gray-400">
By{" "}
{secretApprovalRequestDetails?.committerUser ? (
<>
{secretApprovalRequestDetails?.committerUser?.firstName} (
{secretApprovalRequestDetails?.committerUser?.email})
</>
) : (
<span className="text-gray-500">Deleted User</span>
)}
</p>
</div>
{!hasMerged &&
secretApprovalRequestDetails.status === "open" &&

View File

@ -47,7 +47,6 @@ import { Route as adminEnvironmentPageRouteImport } from './pages/admin/Environm
import { Route as adminEncryptionPageRouteImport } from './pages/admin/EncryptionPage/route'
import { Route as adminCachingPageRouteImport } from './pages/admin/CachingPage/route'
import { Route as adminAuthenticationPageRouteImport } from './pages/admin/AuthenticationPage/route'
import { Route as organizationSsoPageRouteImport } from './pages/organization/SsoPage/route'
import { Route as organizationProjectsPageRouteImport } from './pages/organization/ProjectsPage/route'
import { Route as organizationBillingPageRouteImport } from './pages/organization/BillingPage/route'
import { Route as organizationAuditLogsPageRouteImport } from './pages/organization/AuditLogsPage/route'
@ -591,12 +590,6 @@ const adminAuthenticationPageRouteRoute =
getParentRoute: () => adminLayoutRoute,
} as any)
const organizationSsoPageRouteRoute = organizationSsoPageRouteImport.update({
id: '/sso',
path: '/sso',
getParentRoute: () => AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationProjectsPageRouteRoute =
organizationProjectsPageRouteImport.update({
id: '/projects',
@ -2159,13 +2152,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof organizationProjectsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/sso': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/sso'
path: '/sso'
fullPath: '/organization/sso'
preLoaderRoute: typeof organizationSsoPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': {
id: '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
path: '/authentication'
@ -3428,7 +3414,6 @@ interface AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren {
organizationAuditLogsPageRouteRoute: typeof organizationAuditLogsPageRouteRoute
organizationBillingPageRouteRoute: typeof organizationBillingPageRouteRoute
organizationProjectsPageRouteRoute: typeof organizationProjectsPageRouteRoute
organizationSsoPageRouteRoute: typeof organizationSsoPageRouteRoute
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren
@ -3447,7 +3432,6 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren: Authentica
organizationAuditLogsPageRouteRoute: organizationAuditLogsPageRouteRoute,
organizationBillingPageRouteRoute: organizationBillingPageRouteRoute,
organizationProjectsPageRouteRoute: organizationProjectsPageRouteRoute,
organizationSsoPageRouteRoute: organizationSsoPageRouteRoute,
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute:
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren,
AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute:
@ -4345,7 +4329,6 @@ export interface FileRoutesByFullPath {
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
'/organization/billing': typeof organizationBillingPageRouteRoute
'/organization/projects': typeof organizationProjectsPageRouteRoute
'/organization/sso': typeof organizationSsoPageRouteRoute
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
'/admin/caching': typeof adminCachingPageRouteRoute
'/admin/encryption': typeof adminEncryptionPageRouteRoute
@ -4542,7 +4525,6 @@ export interface FileRoutesByTo {
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
'/organization/billing': typeof organizationBillingPageRouteRoute
'/organization/projects': typeof organizationProjectsPageRouteRoute
'/organization/sso': typeof organizationSsoPageRouteRoute
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
'/admin/caching': typeof adminCachingPageRouteRoute
'/admin/encryption': typeof adminEncryptionPageRouteRoute
@ -4739,7 +4721,6 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/billing': typeof organizationBillingPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/projects': typeof organizationProjectsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/sso': typeof organizationSsoPageRouteRoute
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': typeof adminAuthenticationPageRouteRoute
'/_authenticate/_inject-org-details/admin/_admin-layout/caching': typeof adminCachingPageRouteRoute
'/_authenticate/_inject-org-details/admin/_admin-layout/encryption': typeof adminEncryptionPageRouteRoute
@ -4949,7 +4930,6 @@ export interface FileRouteTypes {
| '/organization/audit-logs'
| '/organization/billing'
| '/organization/projects'
| '/organization/sso'
| '/admin/authentication'
| '/admin/caching'
| '/admin/encryption'
@ -5145,7 +5125,6 @@ export interface FileRouteTypes {
| '/organization/audit-logs'
| '/organization/billing'
| '/organization/projects'
| '/organization/sso'
| '/admin/authentication'
| '/admin/caching'
| '/admin/encryption'
@ -5340,7 +5319,6 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/organization/audit-logs'
| '/_authenticate/_inject-org-details/_org-layout/organization/billing'
| '/_authenticate/_inject-org-details/_org-layout/organization/projects'
| '/_authenticate/_inject-org-details/_org-layout/organization/sso'
| '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
| '/_authenticate/_inject-org-details/admin/_admin-layout/caching'
| '/_authenticate/_inject-org-details/admin/_admin-layout/encryption'
@ -5732,7 +5710,6 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/organization/audit-logs",
"/_authenticate/_inject-org-details/_org-layout/organization/billing",
"/_authenticate/_inject-org-details/_org-layout/organization/projects",
"/_authenticate/_inject-org-details/_org-layout/organization/sso",
"/_authenticate/_inject-org-details/_org-layout/organization/app-connections",
"/_authenticate/_inject-org-details/_org-layout/organization/gateways",
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing",
@ -5782,10 +5759,6 @@ export const routeTree = rootRoute
"filePath": "organization/ProjectsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/sso": {
"filePath": "organization/SsoPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication": {
"filePath": "admin/AuthenticationPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"

View File

@ -31,7 +31,6 @@ const organizationRoutes = route("/organization", [
index("organization/SettingsPage/route.tsx"),
route("/oauth/callback", "organization/SettingsPage/OauthCallbackPage/route.tsx")
]),
route("/sso", "organization/SsoPage/route.tsx"),
route("/groups/$groupId", "organization/GroupDetailsByIDPage/route.tsx"),
route("/members/$membershipId", "organization/UserDetailsByIDPage/route.tsx"),
route("/roles/$roleId", "organization/RoleByIDPage/route.tsx"),