mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-09 15:38:03 -05:00
swap durations to string format & a few db migration changes
This commit is contained in:
@@ -19,7 +19,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
|
||||
t.boolean("isActive").defaultTo(true);
|
||||
|
||||
t.integer("maxRequestTtlSeconds").nullable();
|
||||
t.string("maxRequestTtl").nullable(); // 1hour, 30seconds, etc
|
||||
|
||||
t.jsonb("conditions").notNullable();
|
||||
t.jsonb("constraints").notNullable();
|
||||
@@ -71,11 +71,11 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.uuid("organizationId").notNullable().index();
|
||||
t.foreign("organizationId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
|
||||
t.uuid("policyId").notNullable().index();
|
||||
t.foreign("policyId").references("id").inTable(TableName.ApprovalPolicies).onDelete("CASCADE");
|
||||
t.uuid("policyId").nullable().index();
|
||||
t.foreign("policyId").references("id").inTable(TableName.ApprovalPolicies).onDelete("SET NULL");
|
||||
|
||||
t.uuid("requesterId").notNullable().index();
|
||||
t.foreign("requesterId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||
t.uuid("requesterId").nullable().index();
|
||||
t.foreign("requesterId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||
|
||||
// To be used in the event of requester deletion
|
||||
t.string("requesterName").notNullable();
|
||||
@@ -156,11 +156,11 @@ export async function up(knex: Knex): Promise<void> {
|
||||
t.string("projectId").notNullable().index();
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
|
||||
t.uuid("requestId").notNullable().index();
|
||||
t.foreign("requestId").references("id").inTable(TableName.ApprovalRequests).onDelete("CASCADE");
|
||||
t.uuid("requestId").nullable().index();
|
||||
t.foreign("requestId").references("id").inTable(TableName.ApprovalRequests).onDelete("SET NULL");
|
||||
|
||||
t.uuid("granteeUserId").notNullable().index();
|
||||
t.foreign("granteeUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||
t.uuid("granteeUserId").nullable().index();
|
||||
t.foreign("granteeUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||
|
||||
t.uuid("revokedByUserId").nullable().index();
|
||||
t.foreign("revokedByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||
|
||||
@@ -14,7 +14,7 @@ export const ApprovalPoliciesSchema = z.object({
|
||||
type: z.string(),
|
||||
name: z.string(),
|
||||
isActive: z.boolean().default(true).nullable().optional(),
|
||||
maxRequestTtlSeconds: z.number().nullable().optional(),
|
||||
maxRequestTtl: z.string().nullable().optional(),
|
||||
conditions: z.unknown(),
|
||||
constraints: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
|
||||
@@ -10,8 +10,8 @@ import { TImmutableDBKeys } from "./models";
|
||||
export const ApprovalRequestGrantsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
projectId: z.string(),
|
||||
requestId: z.string().uuid(),
|
||||
granteeUserId: z.string().uuid(),
|
||||
requestId: z.string().uuid().nullable().optional(),
|
||||
granteeUserId: z.string().uuid().nullable().optional(),
|
||||
revokedByUserId: z.string().uuid().nullable().optional(),
|
||||
revocationReason: z.string().nullable().optional(),
|
||||
status: z.string(),
|
||||
|
||||
@@ -11,8 +11,8 @@ export const ApprovalRequestsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
projectId: z.string(),
|
||||
organizationId: z.string().uuid(),
|
||||
policyId: z.string().uuid(),
|
||||
requesterId: z.string().uuid(),
|
||||
policyId: z.string().uuid().nullable().optional(),
|
||||
requesterId: z.string().uuid().nullable().optional(),
|
||||
requesterName: z.string(),
|
||||
requesterEmail: z.string(),
|
||||
type: z.string(),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from "@app/db/schemas";
|
||||
|
||||
import { ApproverType } from "./approval-policy-enums";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
const ApprovalPolicyStepSchema = z.object({
|
||||
name: z.string().min(1).max(128).nullable().optional(),
|
||||
@@ -21,6 +22,16 @@ const ApprovalPolicyStepSchema = z.object({
|
||||
.array()
|
||||
});
|
||||
|
||||
const MaxRequestTtlSchema = z.string().refine(
|
||||
(val) => {
|
||||
const duration = ms(val) / 1000;
|
||||
|
||||
// 1 hour to 30 days
|
||||
return duration >= 3600 && duration <= 2592000;
|
||||
},
|
||||
{ message: "Duration must be between 1 hour and 30 days" }
|
||||
);
|
||||
|
||||
// Policy
|
||||
export const BaseApprovalPolicySchema = ApprovalPoliciesSchema.extend({
|
||||
steps: ApprovalPolicyStepSchema.array()
|
||||
@@ -29,13 +40,13 @@ export const BaseApprovalPolicySchema = ApprovalPoliciesSchema.extend({
|
||||
export const BaseCreateApprovalPolicySchema = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
name: z.string().min(1).max(128),
|
||||
maxRequestTtlSeconds: z.number().min(3600).max(2592000).nullable().optional(), // 1 hour to 30 days
|
||||
maxRequestTtl: MaxRequestTtlSchema.nullable().optional(),
|
||||
steps: ApprovalPolicyStepSchema.array()
|
||||
});
|
||||
|
||||
export const BaseUpdateApprovalPolicySchema = z.object({
|
||||
name: z.string().min(1).max(128).optional(),
|
||||
maxRequestTtlSeconds: z.number().min(3600).max(2592000).nullable().optional(), // 1 hour to 30 days
|
||||
maxRequestTtlSeconds: MaxRequestTtlSchema.nullable().optional(),
|
||||
steps: ApprovalPolicyStepSchema.array().optional()
|
||||
});
|
||||
|
||||
@@ -64,5 +75,17 @@ export const BaseApprovalRequestSchema = ApprovalRequestsSchema.extend({
|
||||
export const BaseCreateApprovalRequestSchema = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
justification: z.string().max(256).nullable().optional(),
|
||||
expiresAt: z.coerce.date().nullable().optional()
|
||||
requestDuration: z
|
||||
.string()
|
||||
.refine(
|
||||
(val) => {
|
||||
const duration = ms(val) / 1000;
|
||||
|
||||
// 1 minute to 30 days
|
||||
return duration >= 60 && duration <= 2592000;
|
||||
},
|
||||
{ message: "Duration must be between 1 minute and 30 days" }
|
||||
)
|
||||
.nullable()
|
||||
.optional()
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
TCreateRequestDTO,
|
||||
TUpdatePolicyDTO
|
||||
} from "./approval-policy-types";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
type TApprovalPolicyServiceFactoryDep = {
|
||||
approvalPolicyDAL: TApprovalPolicyDALFactory;
|
||||
@@ -106,7 +107,7 @@ export const approvalPolicyServiceFactory = ({
|
||||
|
||||
const create = async (
|
||||
policyType: ApprovalPolicyType,
|
||||
{ projectId, name, maxRequestTtlSeconds, conditions, constraints, steps }: TCreatePolicyDTO,
|
||||
{ projectId, name, maxRequestTtl, conditions, constraints, steps }: TCreatePolicyDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const { hasRole } = await permissionService.getProjectPermission({
|
||||
@@ -135,7 +136,7 @@ export const approvalPolicyServiceFactory = ({
|
||||
projectId,
|
||||
organizationId: actor.orgId,
|
||||
name,
|
||||
maxRequestTtlSeconds,
|
||||
maxRequestTtl,
|
||||
conditions: { version: 1, conditions },
|
||||
constraints: { version: 1, constraints },
|
||||
type: policyType
|
||||
@@ -227,7 +228,7 @@ export const approvalPolicyServiceFactory = ({
|
||||
|
||||
const updateById = async (
|
||||
policyId: string,
|
||||
{ name, maxRequestTtlSeconds, conditions, constraints, steps }: TUpdatePolicyDTO,
|
||||
{ name, maxRequestTtl, conditions, constraints, steps }: TUpdatePolicyDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const policy = await approvalPolicyDAL.findById(policyId);
|
||||
@@ -264,8 +265,8 @@ export const approvalPolicyServiceFactory = ({
|
||||
updateDoc.name = name;
|
||||
}
|
||||
|
||||
if (maxRequestTtlSeconds !== undefined) {
|
||||
updateDoc.maxRequestTtlSeconds = maxRequestTtlSeconds;
|
||||
if (maxRequestTtl !== undefined) {
|
||||
updateDoc.maxRequestTtl = maxRequestTtl;
|
||||
}
|
||||
|
||||
if (conditions !== undefined) {
|
||||
@@ -352,7 +353,7 @@ export const approvalPolicyServiceFactory = ({
|
||||
{
|
||||
projectId,
|
||||
requestData,
|
||||
expiresAt,
|
||||
requestDuration,
|
||||
justification,
|
||||
requesterName,
|
||||
requesterEmail
|
||||
@@ -376,18 +377,20 @@ export const approvalPolicyServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "Policy constraints not met" });
|
||||
}
|
||||
|
||||
if (expiresAt) {
|
||||
const now = new Date();
|
||||
const ttlSeconds = (new Date(expiresAt).getTime() - now.getTime()) / 1000;
|
||||
let expiresAt: Date | undefined;
|
||||
|
||||
if (ttlSeconds < 3600) {
|
||||
throw new BadRequestError({ message: "Expiration time must be at least 1 hour in the future" });
|
||||
}
|
||||
if (requestDuration) {
|
||||
const ttlMs = ms(requestDuration);
|
||||
|
||||
if (policy.maxRequestTtlSeconds && ttlSeconds > policy.maxRequestTtlSeconds) {
|
||||
throw new BadRequestError({
|
||||
message: `Expiration time exceeds the maximum allowed TTL of ${policy.maxRequestTtlSeconds} seconds`
|
||||
});
|
||||
expiresAt = new Date(Date.now() + ttlMs);
|
||||
|
||||
if (policy.maxRequestTtl) {
|
||||
const maxTtlMs = ms(policy.maxRequestTtl);
|
||||
if (ttlMs > maxTtlMs) {
|
||||
throw new BadRequestError({
|
||||
message: `Expiration time exceeds the maximum allowed TTL of ${policy.maxRequestTtl}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface ApprovalPolicyStep {
|
||||
export interface TCreatePolicyDTO {
|
||||
projectId: TApprovalPolicy["projectId"];
|
||||
name: TApprovalPolicy["name"];
|
||||
maxRequestTtlSeconds?: TApprovalPolicy["maxRequestTtlSeconds"];
|
||||
maxRequestTtl?: TApprovalPolicy["maxRequestTtl"];
|
||||
conditions: TApprovalPolicy["conditions"]["conditions"];
|
||||
constraints: TApprovalPolicy["constraints"]["constraints"];
|
||||
steps: ApprovalPolicyStep[];
|
||||
@@ -43,7 +43,7 @@ export interface TCreatePolicyDTO {
|
||||
|
||||
export interface TUpdatePolicyDTO {
|
||||
name?: TApprovalPolicy["name"];
|
||||
maxRequestTtlSeconds?: TApprovalPolicy["maxRequestTtlSeconds"];
|
||||
maxRequestTtl?: TApprovalPolicy["maxRequestTtl"];
|
||||
conditions?: TApprovalPolicy["conditions"]["conditions"];
|
||||
constraints?: TApprovalPolicy["constraints"]["constraints"];
|
||||
steps?: ApprovalPolicyStep[];
|
||||
@@ -54,7 +54,7 @@ export interface TCreateRequestDTO {
|
||||
projectId: TApprovalRequest["projectId"];
|
||||
requestData: TApprovalRequest["requestData"]["requestData"];
|
||||
justification?: TApprovalRequest["justification"];
|
||||
expiresAt?: TApprovalRequest["expiresAt"];
|
||||
requestDuration?: string;
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
TApprovalResourceFactory
|
||||
} from "../approval-policy-types";
|
||||
import { TPamAccessPolicy, TPamAccessPolicyInputs, TPamAccessRequestData } from "./pam-access-policy-types";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
export const pamAccessPolicyFactory: TApprovalResourceFactory<
|
||||
TPamAccessPolicyInputs,
|
||||
@@ -84,10 +85,10 @@ export const pamAccessPolicyFactory: TApprovalResourceFactory<
|
||||
policy,
|
||||
inputs
|
||||
) => {
|
||||
const reqDuration = inputs.requestDurationSeconds;
|
||||
const durationConstraint = policy.constraints.constraints.requestDurationSeconds;
|
||||
const reqDuration = ms(inputs.accessDuration);
|
||||
const durationConstraint = policy.constraints.constraints.accessDuration;
|
||||
|
||||
return reqDuration >= durationConstraint.min && reqDuration <= durationConstraint.max;
|
||||
return reqDuration >= ms(durationConstraint.min) && reqDuration <= ms(durationConstraint.max);
|
||||
};
|
||||
|
||||
const postApprovalRoutine: TApprovalRequestFactoryPostApprovalRoutine = async (_request) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { ms } from "@app/lib/ms";
|
||||
|
||||
import {
|
||||
BaseApprovalPolicySchema,
|
||||
@@ -22,11 +23,21 @@ export const PamAccessPolicyConditionsSchema = z
|
||||
})
|
||||
.array();
|
||||
|
||||
const DurationSchema = z.string().refine(
|
||||
(val) => {
|
||||
const duration = ms(val) / 1000;
|
||||
|
||||
// 30 seconds to 7 days
|
||||
return duration >= 30 && duration <= 604800;
|
||||
},
|
||||
{ message: "Duration must be between 30 seconds and 7 days" }
|
||||
);
|
||||
|
||||
// Constraints
|
||||
export const PamAccessPolicyConstraintsSchema = z.object({
|
||||
requestDurationSeconds: z.object({
|
||||
min: z.number().min(30).max(604800),
|
||||
max: z.number().min(30).max(604800) // 30 seconds to 7 days
|
||||
accessDuration: z.object({
|
||||
min: DurationSchema,
|
||||
max: DurationSchema
|
||||
})
|
||||
});
|
||||
|
||||
@@ -34,7 +45,7 @@ export const PamAccessPolicyConstraintsSchema = z.object({
|
||||
export const PamAccessPolicyRequestDataSchema = z.object({
|
||||
resourceId: z.string().uuid(),
|
||||
accountPath: z.string(),
|
||||
requestDurationSeconds: z.number().min(30).max(604800) // 30 seconds to 7 days
|
||||
accessDuration: DurationSchema
|
||||
});
|
||||
|
||||
// Policy
|
||||
|
||||
Reference in New Issue
Block a user