mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 01:29:38 +00:00
Merge branch 'heads/main' into daniel/scim-deprovisioning-ui
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas/models";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "maxTimePeriod"))) {
|
||||
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||
t.string("maxTimePeriod").nullable(); // Ex: 1h - Null is permanent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "maxTimePeriod")) {
|
||||
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||
t.dropColumn("maxTimePeriod");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,8 @@ export const AccessApprovalPoliciesSchema = z.object({
|
||||
updatedAt: z.date(),
|
||||
enforcementLevel: z.string().default("hard"),
|
||||
deletedAt: z.date().nullable().optional(),
|
||||
allowedSelfApprovals: z.boolean().default(true)
|
||||
allowedSelfApprovals: z.boolean().default(true),
|
||||
maxTimePeriod: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;
|
||||
|
||||
@@ -3,12 +3,32 @@ 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 { ms } from "@app/lib/ms";
|
||||
import { EnforcementLevel } from "@app/lib/types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
const maxTimePeriodSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.nullish()
|
||||
.transform((val, ctx) => {
|
||||
if (val === undefined) return undefined;
|
||||
if (!val || val === "permanent") return null;
|
||||
const parsedMs = ms(val);
|
||||
|
||||
if (typeof parsedMs !== "number" || parsedMs <= 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Invalid time period format or value. Must be a positive duration (e.g., '1h', '30m', '2d')."
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return val;
|
||||
});
|
||||
|
||||
export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
@@ -71,7 +91,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.optional(),
|
||||
approvals: z.number().min(1).default(1),
|
||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||
allowedSelfApprovals: z.boolean().default(true)
|
||||
allowedSelfApprovals: z.boolean().default(true),
|
||||
maxTimePeriod: maxTimePeriodSchema
|
||||
})
|
||||
.refine(
|
||||
(val) => Boolean(val.environment) || Boolean(val.environments),
|
||||
@@ -124,7 +145,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
.array()
|
||||
.nullable()
|
||||
.optional(),
|
||||
bypassers: z.object({ type: z.nativeEnum(BypasserType), id: z.string().nullable().optional() }).array()
|
||||
bypassers: z.object({ type: z.nativeEnum(BypasserType), id: z.string().nullable().optional() }).array(),
|
||||
maxTimePeriod: z.string().nullable().optional()
|
||||
})
|
||||
.array()
|
||||
.nullable()
|
||||
@@ -233,7 +255,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
stepNumber: z.number().int()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
.optional(),
|
||||
maxTimePeriod: maxTimePeriodSchema
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -314,7 +337,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
})
|
||||
.array()
|
||||
.nullable()
|
||||
.optional()
|
||||
.optional(),
|
||||
maxTimePeriod: z.string().nullable().optional()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,7 +129,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
envId: z.string(),
|
||||
enforcementLevel: z.string(),
|
||||
deletedAt: z.date().nullish(),
|
||||
allowedSelfApprovals: z.boolean()
|
||||
allowedSelfApprovals: z.boolean(),
|
||||
maxTimePeriod: z.string().nullable().optional()
|
||||
}),
|
||||
reviewers: z
|
||||
.object({
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface TAccessApprovalPolicyDALFactory
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
projectId: string;
|
||||
bypassers: (
|
||||
| {
|
||||
@@ -96,6 +97,7 @@ export interface TAccessApprovalPolicyDALFactory
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
environments: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -141,6 +143,7 @@ export interface TAccessApprovalPolicyDALFactory
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
}
|
||||
| undefined
|
||||
>;
|
||||
|
||||
@@ -100,7 +100,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
environments,
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals,
|
||||
approvalsRequired
|
||||
approvalsRequired,
|
||||
maxTimePeriod
|
||||
}) => {
|
||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||
@@ -219,7 +220,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
secretPath,
|
||||
name,
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals
|
||||
allowedSelfApprovals,
|
||||
maxTimePeriod
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -318,7 +320,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals,
|
||||
approvalsRequired,
|
||||
environments
|
||||
environments,
|
||||
maxTimePeriod
|
||||
}: TUpdateAccessApprovalPolicy) => {
|
||||
const groupApprovers = approvers.filter((approver) => approver.type === ApproverType.Group);
|
||||
|
||||
@@ -461,7 +464,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
secretPath,
|
||||
name,
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals
|
||||
allowedSelfApprovals,
|
||||
maxTimePeriod
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
@@ -41,6 +41,7 @@ export type TCreateAccessApprovalPolicy = {
|
||||
enforcementLevel: EnforcementLevel;
|
||||
allowedSelfApprovals: boolean;
|
||||
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
||||
maxTimePeriod?: string | null;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TUpdateAccessApprovalPolicy = {
|
||||
@@ -60,6 +61,7 @@ export type TUpdateAccessApprovalPolicy = {
|
||||
allowedSelfApprovals: boolean;
|
||||
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
||||
environments?: string[];
|
||||
maxTimePeriod?: string | null;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TDeleteAccessApprovalPolicy = {
|
||||
@@ -104,7 +106,8 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
environment,
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals,
|
||||
approvalsRequired
|
||||
approvalsRequired,
|
||||
maxTimePeriod
|
||||
}: TCreateAccessApprovalPolicy) => Promise<{
|
||||
environment: {
|
||||
name: string;
|
||||
@@ -135,6 +138,7 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
}>;
|
||||
deleteAccessApprovalPolicy: ({
|
||||
policyId,
|
||||
@@ -159,6 +163,7 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
environment: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -185,7 +190,8 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
enforcementLevel,
|
||||
allowedSelfApprovals,
|
||||
approvalsRequired,
|
||||
environments
|
||||
environments,
|
||||
maxTimePeriod
|
||||
}: TUpdateAccessApprovalPolicy) => Promise<{
|
||||
environment: {
|
||||
id: string;
|
||||
@@ -208,6 +214,7 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath?: string | null | undefined;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
}>;
|
||||
getAccessApprovalPolicyByProjectSlug: ({
|
||||
actorId,
|
||||
@@ -242,6 +249,7 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
environment: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -298,6 +306,7 @@ export interface TAccessApprovalPolicyServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
secretPath: string;
|
||||
deletedAt?: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
environment: {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface TAccessApprovalRequestDALFactory extends Omit<TOrmify<TableName
|
||||
enforcementLevel: string;
|
||||
allowedSelfApprovals: boolean;
|
||||
deletedAt: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
};
|
||||
projectId: string;
|
||||
environments: string[];
|
||||
@@ -164,6 +165,7 @@ export interface TAccessApprovalRequestDALFactory extends Omit<TOrmify<TableName
|
||||
allowedSelfApprovals: boolean;
|
||||
envId: string;
|
||||
deletedAt: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
};
|
||||
projectId: string;
|
||||
environment: string;
|
||||
@@ -292,7 +294,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
`requestedByUser.id`
|
||||
)
|
||||
|
||||
// I added these 3:
|
||||
.leftJoin<TOrgMemberships>(
|
||||
db(TableName.OrgMembership).as("approverOrgMembership"),
|
||||
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
||||
@@ -324,10 +325,10 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"),
|
||||
db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt"),
|
||||
|
||||
// Added:
|
||||
db.ref("isActive").withSchema("approverOrgMembership").as("approverIsOrgMembershipActive"),
|
||||
db.ref("isActive").withSchema("approverGroupOrgMembership").as("approverGroupIsOrgMembershipActive"),
|
||||
db.ref("isActive").withSchema("reviewerOrgMembership").as("reviewerIsOrgMembershipActive")
|
||||
db.ref("isActive").withSchema("reviewerOrgMembership").as("reviewerIsOrgMembershipActive"),
|
||||
db.ref("maxTimePeriod").withSchema(TableName.AccessApprovalPolicy).as("policyMaxTimePeriod")
|
||||
)
|
||||
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(db.ref("sequence").withSchema(TableName.AccessApprovalPolicyApprover).as("approverSequence"))
|
||||
@@ -394,7 +395,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
enforcementLevel: doc.policyEnforcementLevel,
|
||||
allowedSelfApprovals: doc.policyAllowedSelfApprovals,
|
||||
envId: doc.policyEnvId,
|
||||
deletedAt: doc.policyDeletedAt
|
||||
deletedAt: doc.policyDeletedAt,
|
||||
maxTimePeriod: doc.policyMaxTimePeriod
|
||||
},
|
||||
requestedByUser: {
|
||||
userId: doc.requestedByUserId,
|
||||
@@ -615,7 +617,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
|
||||
tx.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"),
|
||||
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
||||
tx.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
||||
tx.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt"),
|
||||
tx.ref("maxTimePeriod").withSchema(TableName.AccessApprovalPolicy).as("policyMaxTimePeriod")
|
||||
);
|
||||
|
||||
const findById: TAccessApprovalRequestDALFactory["findById"] = async (id, tx) => {
|
||||
@@ -636,7 +639,8 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
||||
secretPath: el.policySecretPath,
|
||||
enforcementLevel: el.policyEnforcementLevel,
|
||||
allowedSelfApprovals: el.policyAllowedSelfApprovals,
|
||||
deletedAt: el.policyDeletedAt
|
||||
deletedAt: el.policyDeletedAt,
|
||||
maxTimePeriod: el.policyMaxTimePeriod
|
||||
},
|
||||
requestedByUser: {
|
||||
userId: el.requestedByUserId,
|
||||
|
||||
@@ -156,6 +156,15 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
throw new BadRequestError({ message: "The policy linked to this request has been deleted" });
|
||||
}
|
||||
|
||||
// Check if the requested time falls under policy.maxTimePeriod
|
||||
if (policy.maxTimePeriod) {
|
||||
if (!temporaryRange || ms(temporaryRange) > ms(policy.maxTimePeriod)) {
|
||||
throw new BadRequestError({
|
||||
message: `Requested access time range is limited to ${policy.maxTimePeriod} by policy`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const approverIds: string[] = [];
|
||||
const approverGroupIds: string[] = [];
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export interface TAccessApprovalRequestServiceFactory {
|
||||
allowedSelfApprovals: boolean;
|
||||
envId: string;
|
||||
deletedAt: Date | null | undefined;
|
||||
maxTimePeriod?: string | null;
|
||||
};
|
||||
projectId: string;
|
||||
environment: string;
|
||||
|
||||
@@ -45,7 +45,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.CREATE.path)
|
||||
.optional(),
|
||||
// backward compatiability with cli
|
||||
// backward compatibility with cli
|
||||
directory: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -58,7 +58,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
folder: SecretFoldersSchema
|
||||
folder: SecretFoldersSchema.extend({
|
||||
path: z.string()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -130,7 +132,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.UPDATE.path)
|
||||
.optional(),
|
||||
// backward compatiability with cli
|
||||
// backward compatibility with cli
|
||||
directory: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -143,7 +145,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
folder: SecretFoldersSchema
|
||||
folder: SecretFoldersSchema.extend({
|
||||
path: z.string()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -359,7 +363,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
||||
.transform(removeTrailingSlash)
|
||||
.describe(FOLDERS.LIST.path)
|
||||
.optional(),
|
||||
// backward compatiability with cli
|
||||
// backward compatibility with cli
|
||||
directory: z
|
||||
.string()
|
||||
.trim()
|
||||
|
||||
@@ -49,6 +49,19 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
await queueService.stopRepeatableJob(
|
||||
QueueName.AuditLogPrune,
|
||||
QueueJobs.AuditLogPrune,
|
||||
{ pattern: "0 0 * * *", utc: true },
|
||||
QueueName.AuditLogPrune // just a job id
|
||||
);
|
||||
await queueService.stopRepeatableJob(
|
||||
QueueName.DailyResourceCleanUp,
|
||||
QueueJobs.DailyResourceCleanUp,
|
||||
{ pattern: "0 0 * * *", utc: true },
|
||||
QueueName.DailyResourceCleanUp // just a job id
|
||||
);
|
||||
|
||||
await queueService.startPg<QueueName.DailyResourceCleanUp>(
|
||||
QueueJobs.DailyResourceCleanUp,
|
||||
async () => {
|
||||
|
||||
@@ -238,8 +238,16 @@ export const secretFolderServiceFactory = ({
|
||||
return doc;
|
||||
});
|
||||
|
||||
const [folderWithFullPath] = await folderDAL.findSecretPathByFolderIds(projectId, [folder.id]);
|
||||
|
||||
if (!folderWithFullPath) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to retrieve path for folder with ID '${folder.id}'`
|
||||
});
|
||||
}
|
||||
|
||||
await snapshotService.performSnapshot(folder.parentId as string);
|
||||
return folder;
|
||||
return { ...folder, path: folderWithFullPath.path };
|
||||
};
|
||||
|
||||
const updateManyFolders = async ({
|
||||
@@ -496,8 +504,27 @@ export const secretFolderServiceFactory = ({
|
||||
return doc;
|
||||
});
|
||||
|
||||
const foldersWithFullPaths = await folderDAL.findSecretPathByFolderIds(projectId, [newFolder.id, folder.id]);
|
||||
|
||||
const newFolderWithFullPath = foldersWithFullPaths.find((f) => f?.id === newFolder.id);
|
||||
if (!newFolderWithFullPath) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to retrieve path for folder with ID '${newFolder.id}'`
|
||||
});
|
||||
}
|
||||
|
||||
const folderWithFullPath = foldersWithFullPaths.find((f) => f?.id === folder.id);
|
||||
if (!folderWithFullPath) {
|
||||
throw new NotFoundError({
|
||||
message: `Failed to retrieve path for folder with ID '${folder.id}'`
|
||||
});
|
||||
}
|
||||
|
||||
await snapshotService.performSnapshot(newFolder.parentId as string);
|
||||
return { folder: newFolder, old: folder };
|
||||
return {
|
||||
folder: { ...newFolder, path: newFolderWithFullPath.path },
|
||||
old: { ...folder, path: folderWithFullPath.path }
|
||||
};
|
||||
};
|
||||
|
||||
const $checkFolderPolicy = async ({
|
||||
|
||||
Reference in New Issue
Block a user