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