mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
partial factory, endpoint, and service implementation
This commit is contained in:
2
backend/src/@types/fastify.d.ts
vendored
2
backend/src/@types/fastify.d.ts
vendored
@@ -55,6 +55,7 @@ import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
||||
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
|
||||
import { TApprovalPolicyServiceFactory } from "@app/services/approval-policy/approval-policy-service";
|
||||
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
|
||||
import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
|
||||
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
||||
@@ -361,6 +362,7 @@ declare module "fastify" {
|
||||
convertor: TConvertorServiceFactory;
|
||||
subOrganization: TSubOrgServiceFactory;
|
||||
pkiAlertV2: TPkiAlertV2ServiceFactory;
|
||||
approvalPolicy: TApprovalPolicyServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// everywhere else access using service layer
|
||||
|
||||
50
backend/src/@types/knex.d.ts
vendored
50
backend/src/@types/knex.d.ts
vendored
@@ -26,6 +26,30 @@ import {
|
||||
TAppConnections,
|
||||
TAppConnectionsInsert,
|
||||
TAppConnectionsUpdate,
|
||||
TApprovalPolicies,
|
||||
TApprovalPoliciesInsert,
|
||||
TApprovalPoliciesUpdate,
|
||||
TApprovalPolicyStepApprovers,
|
||||
TApprovalPolicyStepApproversInsert,
|
||||
TApprovalPolicyStepApproversUpdate,
|
||||
TApprovalPolicySteps,
|
||||
TApprovalPolicyStepsInsert,
|
||||
TApprovalPolicyStepsUpdate,
|
||||
TApprovalRequestApprovals,
|
||||
TApprovalRequestApprovalsInsert,
|
||||
TApprovalRequestApprovalsUpdate,
|
||||
TApprovalRequestGrants,
|
||||
TApprovalRequestGrantsInsert,
|
||||
TApprovalRequestGrantsUpdate,
|
||||
TApprovalRequests,
|
||||
TApprovalRequestsInsert,
|
||||
TApprovalRequestStepEligibleApprovers,
|
||||
TApprovalRequestStepEligibleApproversInsert,
|
||||
TApprovalRequestStepEligibleApproversUpdate,
|
||||
TApprovalRequestSteps,
|
||||
TApprovalRequestStepsInsert,
|
||||
TApprovalRequestStepsUpdate,
|
||||
TApprovalRequestsUpdate,
|
||||
TAuditLogs,
|
||||
TAuditLogsInsert,
|
||||
TAuditLogStreams,
|
||||
@@ -571,31 +595,7 @@ import {
|
||||
TWebhooksUpdate,
|
||||
TWorkflowIntegrations,
|
||||
TWorkflowIntegrationsInsert,
|
||||
TWorkflowIntegrationsUpdate,
|
||||
TApprovalPolicies,
|
||||
TApprovalPoliciesInsert,
|
||||
TApprovalPoliciesUpdate,
|
||||
TApprovalPolicyStepApprovers,
|
||||
TApprovalPolicyStepApproversInsert,
|
||||
TApprovalPolicyStepApproversUpdate,
|
||||
TApprovalPolicySteps,
|
||||
TApprovalPolicyStepsInsert,
|
||||
TApprovalPolicyStepsUpdate,
|
||||
TApprovalRequestApprovals,
|
||||
TApprovalRequestApprovalsInsert,
|
||||
TApprovalRequestApprovalsUpdate,
|
||||
TApprovalRequestGrants,
|
||||
TApprovalRequestGrantsInsert,
|
||||
TApprovalRequestGrantsUpdate,
|
||||
TApprovalRequestStepEligibleApprovers,
|
||||
TApprovalRequestStepEligibleApproversInsert,
|
||||
TApprovalRequestStepEligibleApproversUpdate,
|
||||
TApprovalRequestSteps,
|
||||
TApprovalRequestStepsInsert,
|
||||
TApprovalRequestStepsUpdate,
|
||||
TApprovalRequests,
|
||||
TApprovalRequestsInsert,
|
||||
TApprovalRequestsUpdate
|
||||
TWorkflowIntegrationsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import {
|
||||
TCertificateRequests,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
|
||||
@@ -6,6 +6,14 @@ export * from "./access-approval-requests-reviewers";
|
||||
export * from "./additional-privileges";
|
||||
export * from "./api-keys";
|
||||
export * from "./app-connections";
|
||||
export * from "./approval-policies";
|
||||
export * from "./approval-policy-step-approvers";
|
||||
export * from "./approval-policy-steps";
|
||||
export * from "./approval-request-approvals";
|
||||
export * from "./approval-request-grants";
|
||||
export * from "./approval-request-step-eligible-approvers";
|
||||
export * from "./approval-request-steps";
|
||||
export * from "./approval-requests";
|
||||
export * from "./audit-log-streams";
|
||||
export * from "./audit-logs";
|
||||
export * from "./auth-token-sessions";
|
||||
@@ -195,11 +203,3 @@ export * from "./users";
|
||||
export * from "./vault-external-migration-configs";
|
||||
export * from "./webhooks";
|
||||
export * from "./workflow-integrations";
|
||||
export * from "./approval-policies";
|
||||
export * from "./approval-policy-step-approvers";
|
||||
export * from "./approval-policy-steps";
|
||||
export * from "./approval-request-approvals";
|
||||
export * from "./approval-request-grants";
|
||||
export * from "./approval-request-step-eligible-approvers";
|
||||
export * from "./approval-request-steps";
|
||||
export * from "./approval-requests";
|
||||
|
||||
@@ -10,9 +10,21 @@ import {
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||
import { BadRequestError, DatabaseError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import {
|
||||
BadRequestError,
|
||||
DatabaseError,
|
||||
ForbiddenRequestError,
|
||||
NotFoundError,
|
||||
PolicyViolationError
|
||||
} from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import {
|
||||
TApprovalPolicyDALFactory,
|
||||
TApprovalRequestGrantsDALFactory
|
||||
} from "@app/services/approval-policy/approval-policy-dal";
|
||||
import { ApprovalPolicyType } from "@app/services/approval-policy/approval-policy-enums";
|
||||
import { APPROVAL_POLICY_FACTORY_MAP } from "@app/services/approval-policy/approval-policy-factory";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
@@ -51,6 +63,8 @@ type TPamAccountServiceFactoryDep = {
|
||||
>;
|
||||
userDAL: TUserDALFactory;
|
||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||
approvalPolicyDAL: TApprovalPolicyDALFactory;
|
||||
approvalRequestGrantsDAL: TApprovalRequestGrantsDALFactory;
|
||||
};
|
||||
export type TPamAccountServiceFactory = ReturnType<typeof pamAccountServiceFactory>;
|
||||
|
||||
@@ -67,7 +81,9 @@ export const pamAccountServiceFactory = ({
|
||||
licenseService,
|
||||
kmsService,
|
||||
gatewayV2Service,
|
||||
auditLogService
|
||||
auditLogService,
|
||||
approvalPolicyDAL,
|
||||
approvalRequestGrantsDAL
|
||||
}: TPamAccountServiceFactoryDep) => {
|
||||
const create = async (
|
||||
{
|
||||
@@ -531,23 +547,50 @@ export const pamAccountServiceFactory = ({
|
||||
const resource = await pamResourceDAL.findById(account.resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${account.resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
const fac = APPROVAL_POLICY_FACTORY_MAP[ApprovalPolicyType.PamAccess](ApprovalPolicyType.PamAccess);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Access,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: account.name,
|
||||
accountPath: folderPath
|
||||
})
|
||||
);
|
||||
const inputs = {
|
||||
resourceId: resource.id,
|
||||
accountPath: folderPath
|
||||
};
|
||||
|
||||
const canAccess = await fac.canAccess(approvalRequestGrantsDAL, actor.id, resource.projectId, inputs);
|
||||
|
||||
if (canAccess) {
|
||||
// Grant exists, allow access without checking permission
|
||||
} else {
|
||||
const policy = await fac.matchPolicy(approvalPolicyDAL, resource.projectId, inputs);
|
||||
|
||||
if (policy) {
|
||||
throw new PolicyViolationError({
|
||||
message: "A policy is in place for this resource",
|
||||
details: {
|
||||
policyId: policy.id,
|
||||
policyName: policy.name,
|
||||
policyType: policy.type
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If there isn't a policy in place, continue with checking permission
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: account.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Access,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: account.name,
|
||||
accountPath: folderPath
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const session = await pamSessionDAL.create({
|
||||
accountName: account.name,
|
||||
|
||||
@@ -183,3 +183,23 @@ export class CryptographyError extends Error {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
export class PolicyViolationError extends Error {
|
||||
name: string;
|
||||
|
||||
error: unknown;
|
||||
|
||||
details?: unknown;
|
||||
|
||||
constructor({
|
||||
name,
|
||||
error,
|
||||
message,
|
||||
details
|
||||
}: { message?: string; name?: string; error?: unknown; details?: unknown } = {}) {
|
||||
super(message || "A policy is in place for this resource");
|
||||
this.name = name || "PolicyViolationError";
|
||||
this.error = error;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
NotFoundError,
|
||||
OidcAuthError,
|
||||
PermissionBoundaryError,
|
||||
PolicyViolationError,
|
||||
RateLimitError,
|
||||
ScimRequestError,
|
||||
UnauthorizedError
|
||||
@@ -255,6 +256,14 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider
|
||||
detail: error.message
|
||||
// TODO: add subproblems if they exist
|
||||
});
|
||||
} else if (error instanceof PolicyViolationError) {
|
||||
void res.status(HttpStatusCodes.Forbidden).send({
|
||||
reqId: req.id,
|
||||
statusCode: HttpStatusCodes.Forbidden,
|
||||
error: "PolicyViolationError",
|
||||
message: error.message,
|
||||
details: error.details
|
||||
});
|
||||
} else {
|
||||
void res.status(HttpStatusCodes.InternalServerError).send({
|
||||
reqId: req.id,
|
||||
|
||||
@@ -159,6 +159,13 @@ import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
|
||||
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { appConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
|
||||
import {
|
||||
approvalPolicyDALFactory,
|
||||
approvalPolicyStepApproversDALFactory,
|
||||
approvalPolicyStepsDALFactory,
|
||||
approvalRequestGrantsDALFactory
|
||||
} from "@app/services/approval-policy/approval-policy-dal";
|
||||
import { approvalPolicyServiceFactory } from "@app/services/approval-policy/approval-policy-service";
|
||||
import { authDALFactory } from "@app/services/auth/auth-dal";
|
||||
import { authLoginServiceFactory } from "@app/services/auth/auth-login-service";
|
||||
import { authPaswordServiceFactory } from "@app/services/auth/auth-password-service";
|
||||
@@ -2412,6 +2419,9 @@ export const registerRoutes = async (
|
||||
gatewayV2Service
|
||||
});
|
||||
|
||||
const approvalRequestGrantsDAL = approvalRequestGrantsDALFactory(db);
|
||||
const approvalPolicyDAL = approvalPolicyDALFactory(db);
|
||||
|
||||
const pamAccountService = pamAccountServiceFactory({
|
||||
pamAccountDAL,
|
||||
gatewayV2Service,
|
||||
@@ -2423,7 +2433,9 @@ export const registerRoutes = async (
|
||||
permissionService,
|
||||
projectDAL,
|
||||
userDAL,
|
||||
auditLogService
|
||||
auditLogService,
|
||||
approvalRequestGrantsDAL,
|
||||
approvalPolicyDAL
|
||||
});
|
||||
|
||||
const pamAccountRotation = pamAccountRotationServiceFactory({
|
||||
@@ -2451,6 +2463,17 @@ export const registerRoutes = async (
|
||||
auditLogService
|
||||
});
|
||||
|
||||
const approvalPolicyStepsDAL = approvalPolicyStepsDALFactory(db);
|
||||
const approvalPolicyStepApproversDAL = approvalPolicyStepApproversDALFactory(db);
|
||||
|
||||
const approvalPolicyService = approvalPolicyServiceFactory({
|
||||
approvalPolicyDAL,
|
||||
approvalPolicyStepsDAL,
|
||||
approvalPolicyStepApproversDAL,
|
||||
projectDAL,
|
||||
permissionService
|
||||
});
|
||||
|
||||
// setup the communication with license key server
|
||||
await licenseService.init();
|
||||
|
||||
@@ -2630,7 +2653,8 @@ export const registerRoutes = async (
|
||||
additionalPrivilege: additionalPrivilegeService,
|
||||
identityProject: identityProjectService,
|
||||
convertor: convertorService,
|
||||
pkiAlertV2: pkiAlertV2Service
|
||||
pkiAlertV2: pkiAlertV2Service,
|
||||
approvalPolicy: approvalPolicyService
|
||||
});
|
||||
|
||||
const cronJobs: CronJob[] = [];
|
||||
|
||||
@@ -16,8 +16,8 @@ import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
||||
import { registerChecklyConnectionRouter } from "./checkly-connection-router";
|
||||
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||
import { registerDNSMadeEasyConnectionRouter } from "./dns-made-easy-connection-router";
|
||||
import { registerDigitalOceanConnectionRouter } from "./digital-ocean-connection-router";
|
||||
import { registerDNSMadeEasyConnectionRouter } from "./dns-made-easy-connection-router";
|
||||
import { registerFlyioConnectionRouter } from "./flyio-connection-router";
|
||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ApprovalPolicyType } from "@app/services/approval-policy/approval-policy-enums";
|
||||
import {
|
||||
TApprovalPolicy,
|
||||
TCreatePolicyDTO,
|
||||
TUpdatePolicyDTO
|
||||
} from "@app/services/approval-policy/approval-policy-types";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerApprovalPolicyEndpoints = <P extends TApprovalPolicy>({
|
||||
server,
|
||||
policyType,
|
||||
createPolicySchema,
|
||||
updatePolicySchema,
|
||||
policyResponseSchema
|
||||
}: {
|
||||
server: FastifyZodProvider;
|
||||
policyType: ApprovalPolicyType;
|
||||
createPolicySchema: z.ZodType<
|
||||
TCreatePolicyDTO & {
|
||||
conditions: P["conditions"]["conditions"];
|
||||
constraints: P["constraints"]["constraints"];
|
||||
}
|
||||
>;
|
||||
updatePolicySchema: z.ZodType<
|
||||
TUpdatePolicyDTO & {
|
||||
conditions?: P["conditions"]["conditions"];
|
||||
constraints?: P["constraints"]["constraints"];
|
||||
}
|
||||
>;
|
||||
policyResponseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create approval policy",
|
||||
body: createPolicySchema,
|
||||
response: {
|
||||
200: z.object({
|
||||
policy: policyResponseSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const policy = await server.services.approvalPolicy.create(policyType, req.body, req.permission);
|
||||
|
||||
// TODO: Audit log
|
||||
|
||||
return { policy };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:policyId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update approval policy",
|
||||
params: z.object({
|
||||
policyId: z.string().uuid()
|
||||
}),
|
||||
body: updatePolicySchema,
|
||||
response: {
|
||||
200: z.object({
|
||||
policy: policyResponseSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const policy = await server.services.approvalPolicy.updateById(req.params.policyId, req.body, req.permission);
|
||||
|
||||
// TODO: Audit log
|
||||
|
||||
return { policy };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:policyId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete approval policy",
|
||||
params: z.object({
|
||||
policyId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
policy: policyResponseSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const policy = await server.services.approvalPolicy.deleteById(req.params.policyId, req.permission);
|
||||
|
||||
// TODO: Audit log
|
||||
|
||||
return { policy };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ApprovalPolicyType } from "@app/services/approval-policy/approval-policy-enums";
|
||||
import {
|
||||
CreatePamAccessPolicySchema,
|
||||
PamAccessPolicySchema,
|
||||
UpdatePamAccessPolicySchema
|
||||
} from "@app/services/approval-policy/pam-access/pam-access-policy-schemas";
|
||||
|
||||
import { registerApprovalPolicyEndpoints } from "./approval-policy-endpoints";
|
||||
|
||||
export const APPROVAL_POLICY_REGISTER_ROUTER_MAP: Record<
|
||||
ApprovalPolicyType,
|
||||
(server: FastifyZodProvider) => Promise<void>
|
||||
> = {
|
||||
[ApprovalPolicyType.PamAccess]: async (server: FastifyZodProvider) => {
|
||||
registerApprovalPolicyEndpoints({
|
||||
server,
|
||||
policyType: ApprovalPolicyType.PamAccess,
|
||||
createPolicySchema: CreatePamAccessPolicySchema,
|
||||
updatePolicySchema: UpdatePamAccessPolicySchema,
|
||||
policyResponseSchema: PamAccessPolicySchema
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router"
|
||||
import { registerSecretSyncRouter, SECRET_SYNC_REGISTER_ROUTER_MAP } from "@app/server/routes/v1/secret-sync-routers";
|
||||
|
||||
import { registerAdminRouter } from "./admin-router";
|
||||
import { APPROVAL_POLICY_REGISTER_ROUTER_MAP } from "./approval-policy-routers";
|
||||
import { registerAuthRoutes } from "./auth-router";
|
||||
import { registerProjectBotRouter } from "./bot-router";
|
||||
import { registerCaRouter } from "./certificate-authority-router";
|
||||
@@ -275,4 +276,14 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
|
||||
await server.register(registerEventRouter, { prefix: "/events" });
|
||||
await server.register(registerUpgradePathRouter, { prefix: "/upgrade-path" });
|
||||
|
||||
await server.register(
|
||||
async (approvalPolicyRouter) => {
|
||||
// Register policy type-specific endpoints
|
||||
for await (const [type, router] of Object.entries(APPROVAL_POLICY_REGISTER_ROUTER_MAP)) {
|
||||
await approvalPolicyRouter.register(router, { prefix: `/${type}` });
|
||||
}
|
||||
},
|
||||
{ prefix: "/approval-policies" }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -72,11 +72,11 @@ import { checklyConnectionService } from "./checkly/checkly-connection-service";
|
||||
import { ValidateCloudflareConnectionCredentialsSchema } from "./cloudflare/cloudflare-connection-schema";
|
||||
import { cloudflareConnectionService } from "./cloudflare/cloudflare-connection-service";
|
||||
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
|
||||
import { ValidateDNSMadeEasyConnectionCredentialsSchema } from "./dns-made-easy/dns-made-easy-connection-schema";
|
||||
import { dnsMadeEasyConnectionService } from "./dns-made-easy/dns-made-easy-connection-service";
|
||||
import { databricksConnectionService } from "./databricks/databricks-connection-service";
|
||||
import { ValidateDigitalOceanConnectionCredentialsSchema } from "./digital-ocean";
|
||||
import { digitalOceanAppPlatformConnectionService } from "./digital-ocean/digital-ocean-connection-service";
|
||||
import { ValidateDNSMadeEasyConnectionCredentialsSchema } from "./dns-made-easy/dns-made-easy-connection-schema";
|
||||
import { dnsMadeEasyConnectionService } from "./dns-made-easy/dns-made-easy-connection-service";
|
||||
import { ValidateFlyioConnectionCredentialsSchema } from "./flyio";
|
||||
import { flyioConnectionService } from "./flyio/flyio-connection-service";
|
||||
import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
TOracleDBConnectionInput,
|
||||
TValidateOracleDBConnectionCredentialsSchema
|
||||
} from "@app/ee/services/app-connections/oracledb";
|
||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
34
backend/src/services/approval-policy/approval-policy-dal.ts
Normal file
34
backend/src/services/approval-policy/approval-policy-dal.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
// Approval Policy
|
||||
export type TApprovalPolicyDALFactory = ReturnType<typeof approvalPolicyDALFactory>;
|
||||
export const approvalPolicyDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ApprovalPolicies);
|
||||
|
||||
return {
|
||||
...orm
|
||||
};
|
||||
};
|
||||
|
||||
// Approval Policy Steps
|
||||
export type TApprovalPolicyStepsDALFactory = ReturnType<typeof approvalPolicyStepsDALFactory>;
|
||||
export const approvalPolicyStepsDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ApprovalPolicySteps);
|
||||
return orm;
|
||||
};
|
||||
|
||||
// Approval Policy Step Approvers
|
||||
export type TApprovalPolicyStepApproversDALFactory = ReturnType<typeof approvalPolicyStepApproversDALFactory>;
|
||||
export const approvalPolicyStepApproversDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ApprovalPolicyStepApprovers);
|
||||
return orm;
|
||||
};
|
||||
|
||||
// Approval Policy Grants
|
||||
export type TApprovalRequestGrantsDALFactory = ReturnType<typeof approvalRequestGrantsDALFactory>;
|
||||
export const approvalRequestGrantsDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.ApprovalRequestGrants);
|
||||
return orm;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
export enum ApprovalPolicyType {
|
||||
PamAccess = "pam-access"
|
||||
}
|
||||
|
||||
export enum ApproverType {
|
||||
Group = "group",
|
||||
User = "user"
|
||||
}
|
||||
|
||||
export enum ApprovalRequestGrantStatus {
|
||||
Active = "active",
|
||||
Expired = "expired",
|
||||
Revoked = "revoked"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ApprovalPolicyType } from "./approval-policy-enums";
|
||||
import { TApprovalPolicy, TApprovalPolicyInputs, TApprovalResourceFactory } from "./approval-policy-types";
|
||||
import { pamAccessPolicyFactory } from "./pam-access/pam-access-policy-factory";
|
||||
|
||||
type TApprovalPolicyFactoryImplementation = TApprovalResourceFactory<TApprovalPolicyInputs, TApprovalPolicy>;
|
||||
|
||||
export const APPROVAL_POLICY_FACTORY_MAP: Record<ApprovalPolicyType, TApprovalPolicyFactoryImplementation> = {
|
||||
[ApprovalPolicyType.PamAccess]: pamAccessPolicyFactory as TApprovalPolicyFactoryImplementation
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ApprovalPoliciesSchema } from "@app/db/schemas";
|
||||
|
||||
import { ApproverType } from "./approval-policy-enums";
|
||||
|
||||
export const BaseApprovalPolicySchema = ApprovalPoliciesSchema;
|
||||
|
||||
export const BaseCreateApprovalPolicySchema = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
organizationId: z.string().uuid(),
|
||||
name: z.string().min(1).max(128),
|
||||
maxRequestTtlSeconds: z.number().min(3600).max(2592000).nullable().optional(), // 1 hour to 30 days
|
||||
steps: z
|
||||
.object({
|
||||
name: z.string().min(1).max(128).nullable().optional(),
|
||||
requiredApprovals: z.number().min(1).max(100),
|
||||
notifyApprovers: z.boolean().optional(),
|
||||
approvers: z
|
||||
.object({
|
||||
type: z.nativeEnum(ApproverType),
|
||||
id: z.string().uuid()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
.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
|
||||
steps: z
|
||||
.object({
|
||||
name: z.string().min(1).max(128).nullable().optional(),
|
||||
requiredApprovals: z.number().min(1).max(100),
|
||||
notifyApprovers: z.boolean().optional(),
|
||||
approvers: z
|
||||
.object({
|
||||
type: z.nativeEnum(ApproverType),
|
||||
id: z.string().uuid()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
});
|
||||
217
backend/src/services/approval-policy/approval-policy-service.ts
Normal file
217
backend/src/services/approval-policy/approval-policy-service.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { ActionProjectType, ProjectMembershipRole, TApprovalPolicies } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
import {
|
||||
TApprovalPolicyDALFactory,
|
||||
TApprovalPolicyStepApproversDALFactory,
|
||||
TApprovalPolicyStepsDALFactory
|
||||
} from "./approval-policy-dal";
|
||||
import { ApprovalPolicyType, ApproverType } from "./approval-policy-enums";
|
||||
import { TCreatePolicyDTO, TUpdatePolicyDTO } from "./approval-policy-types";
|
||||
|
||||
type TApprovalPolicyServiceFactoryDep = {
|
||||
approvalPolicyDAL: TApprovalPolicyDALFactory;
|
||||
approvalPolicyStepsDAL: TApprovalPolicyStepsDALFactory;
|
||||
approvalPolicyStepApproversDAL: TApprovalPolicyStepApproversDALFactory;
|
||||
projectDAL: TProjectDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||
};
|
||||
export type TApprovalPolicyServiceFactory = ReturnType<typeof approvalPolicyServiceFactory>;
|
||||
|
||||
export const approvalPolicyServiceFactory = ({
|
||||
approvalPolicyDAL,
|
||||
approvalPolicyStepsDAL,
|
||||
approvalPolicyStepApproversDAL,
|
||||
permissionService
|
||||
}: TApprovalPolicyServiceFactoryDep) => {
|
||||
const create = async (
|
||||
policyType: ApprovalPolicyType,
|
||||
{ projectId, organizationId, name, maxRequestTtlSeconds, conditions, constraints, steps }: TCreatePolicyDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const { hasRole } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
|
||||
if (!hasRole(ProjectMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "User has insufficient privileges" });
|
||||
}
|
||||
|
||||
const policy = await approvalPolicyDAL.transaction(async (tx) => {
|
||||
const newPolicy = await approvalPolicyDAL.create(
|
||||
{
|
||||
projectId,
|
||||
organizationId,
|
||||
name,
|
||||
maxRequestTtlSeconds,
|
||||
conditions: { version: 1, conditions },
|
||||
constraints: { version: 1, constraints },
|
||||
type: policyType
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
// Create policy steps and their approvers
|
||||
await Promise.all(
|
||||
steps.map(async (step, i) => {
|
||||
const newStep = await approvalPolicyStepsDAL.create(
|
||||
{
|
||||
policyId: newPolicy.id,
|
||||
requiredApprovals: step.requiredApprovals,
|
||||
stepNumber: i + 1,
|
||||
name: step.name,
|
||||
notifyApprovers: step.notifyApprovers
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
if (step.approvers?.length) {
|
||||
await Promise.all(
|
||||
step.approvers.map((approver) =>
|
||||
approvalPolicyStepApproversDAL.create(
|
||||
{
|
||||
policyStepId: newStep.id,
|
||||
userId: approver.type === ApproverType.User ? approver.id : null,
|
||||
groupId: approver.type === ApproverType.Group ? approver.id : null
|
||||
},
|
||||
tx
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return newPolicy;
|
||||
});
|
||||
|
||||
return {
|
||||
policy
|
||||
};
|
||||
};
|
||||
|
||||
const updateById = async (
|
||||
policyId: string,
|
||||
{ name, maxRequestTtlSeconds, conditions, constraints, steps }: TUpdatePolicyDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const policy = await approvalPolicyDAL.findById(policyId);
|
||||
if (!policy) {
|
||||
throw new ForbiddenRequestError({ message: "Policy not found" });
|
||||
}
|
||||
|
||||
const { hasRole } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: policy.projectId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
|
||||
if (!hasRole(ProjectMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "User has insufficient privileges" });
|
||||
}
|
||||
|
||||
const updatedPolicy = await approvalPolicyDAL.transaction(async (tx) => {
|
||||
const updateDoc: Partial<TApprovalPolicies> = {};
|
||||
|
||||
if (name !== undefined) {
|
||||
updateDoc.name = name;
|
||||
}
|
||||
|
||||
if (maxRequestTtlSeconds !== undefined) {
|
||||
updateDoc.maxRequestTtlSeconds = maxRequestTtlSeconds;
|
||||
}
|
||||
|
||||
if (conditions !== undefined) {
|
||||
updateDoc.conditions = { version: 1, conditions };
|
||||
}
|
||||
|
||||
if (constraints !== undefined) {
|
||||
updateDoc.constraints = { version: 1, constraints };
|
||||
}
|
||||
|
||||
const updated = await approvalPolicyDAL.updateById(policyId, updateDoc, tx);
|
||||
|
||||
if (steps !== undefined) {
|
||||
await approvalPolicyStepsDAL.delete({ policyId }, tx);
|
||||
|
||||
await Promise.all(
|
||||
steps.map(async (step, i) => {
|
||||
const newStep = await approvalPolicyStepsDAL.create(
|
||||
{
|
||||
policyId,
|
||||
requiredApprovals: step.requiredApprovals,
|
||||
stepNumber: i + 1,
|
||||
name: step.name,
|
||||
notifyApprovers: step.notifyApprovers
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
if (step.approvers?.length) {
|
||||
await Promise.all(
|
||||
step.approvers.map((approver) =>
|
||||
approvalPolicyStepApproversDAL.create(
|
||||
{
|
||||
policyStepId: newStep.id,
|
||||
userId: approver.type === ApproverType.User ? approver.id : null,
|
||||
groupId: approver.type === ApproverType.Group ? approver.id : null
|
||||
},
|
||||
tx
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
|
||||
return {
|
||||
policy: updatedPolicy
|
||||
};
|
||||
};
|
||||
|
||||
const deleteById = async (policyId: string, actor: OrgServiceActor) => {
|
||||
const policy = await approvalPolicyDAL.findById(policyId);
|
||||
if (!policy) {
|
||||
throw new ForbiddenRequestError({ message: "Policy not found" });
|
||||
}
|
||||
|
||||
const { hasRole } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: policy.projectId,
|
||||
actionProjectType: ActionProjectType.Any
|
||||
});
|
||||
|
||||
if (!hasRole(ProjectMembershipRole.Admin)) {
|
||||
throw new ForbiddenRequestError({ message: "User has insufficient privileges" });
|
||||
}
|
||||
|
||||
const deletedPolicy = await approvalPolicyDAL.deleteById(policyId);
|
||||
|
||||
return {
|
||||
policy: deletedPolicy
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
create,
|
||||
updateById,
|
||||
deleteById
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import { TApprovalPolicyDALFactory, TApprovalRequestGrantsDALFactory } from "./approval-policy-dal";
|
||||
import { ApprovalPolicyType, ApproverType } from "./approval-policy-enums";
|
||||
import {
|
||||
TPamAccessPolicy,
|
||||
TPamAccessPolicyConditions,
|
||||
TPamAccessPolicyConstraints,
|
||||
TPamAccessPolicyInputs
|
||||
} from "./pam-access/pam-access-policy-types";
|
||||
|
||||
export type TApprovalPolicy = TPamAccessPolicy;
|
||||
export type TApprovalPolicyInputs = TPamAccessPolicyInputs;
|
||||
export type TApprovalPolicyConditions = TPamAccessPolicyConditions;
|
||||
export type TApprovalPolicyConstraints = TPamAccessPolicyConstraints;
|
||||
|
||||
// DTOs
|
||||
export interface TCreatePolicyDTO {
|
||||
projectId: TApprovalPolicy["projectId"];
|
||||
organizationId: TApprovalPolicy["organizationId"];
|
||||
name: TApprovalPolicy["name"];
|
||||
maxRequestTtlSeconds?: TApprovalPolicy["maxRequestTtlSeconds"];
|
||||
conditions: TApprovalPolicy["conditions"]["conditions"];
|
||||
constraints: TApprovalPolicy["constraints"]["constraints"];
|
||||
steps: {
|
||||
name?: string | null;
|
||||
requiredApprovals: number;
|
||||
notifyApprovers?: boolean;
|
||||
approvers: {
|
||||
type: ApproverType.User | ApproverType.Group;
|
||||
id: string;
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface TUpdatePolicyDTO {
|
||||
name?: TApprovalPolicy["name"];
|
||||
maxRequestTtlSeconds?: TApprovalPolicy["maxRequestTtlSeconds"];
|
||||
conditions?: TApprovalPolicy["conditions"]["conditions"];
|
||||
constraints?: TApprovalPolicy["constraints"]["constraints"];
|
||||
steps?: {
|
||||
name?: string | null;
|
||||
requiredApprovals: number;
|
||||
notifyApprovers?: boolean;
|
||||
approvers: {
|
||||
type: ApproverType.User | ApproverType.Group;
|
||||
id: string;
|
||||
}[];
|
||||
}[];
|
||||
}
|
||||
|
||||
// Factory
|
||||
export type TApprovalRequestFactoryMatchPolicy<I extends TApprovalPolicyInputs, P extends TApprovalPolicy> = (
|
||||
approvalPolicyDAL: TApprovalPolicyDALFactory,
|
||||
projectId: string,
|
||||
inputs: I
|
||||
) => Promise<P | null>;
|
||||
export type TApprovalRequestFactoryCanAccess<I extends TApprovalPolicyInputs> = (
|
||||
approvalRequestGrantsDAL: TApprovalRequestGrantsDALFactory,
|
||||
projectId: string,
|
||||
userId: string,
|
||||
inputs: I
|
||||
) => Promise<boolean>;
|
||||
|
||||
export type TApprovalResourceFactory<I extends TApprovalPolicyInputs, P extends TApprovalPolicy> = (
|
||||
policyType: ApprovalPolicyType
|
||||
) => {
|
||||
matchPolicy: TApprovalRequestFactoryMatchPolicy<I, P>;
|
||||
canAccess: TApprovalRequestFactoryCanAccess<I>;
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
import picomatch from "picomatch";
|
||||
|
||||
import { ApprovalRequestGrantStatus } from "../approval-policy-enums";
|
||||
import {
|
||||
TApprovalRequestFactoryCanAccess,
|
||||
TApprovalRequestFactoryMatchPolicy,
|
||||
TApprovalResourceFactory
|
||||
} from "../approval-policy-types";
|
||||
import { TPamAccessPolicy, TPamAccessPolicyInputs } from "./pam-access-policy-types";
|
||||
|
||||
export const pamAccessPolicyFactory: TApprovalResourceFactory<TPamAccessPolicyInputs, TPamAccessPolicy> = (
|
||||
policyType
|
||||
) => {
|
||||
const matchPolicy: TApprovalRequestFactoryMatchPolicy<TPamAccessPolicyInputs, TPamAccessPolicy> = async (
|
||||
approvalPolicyDAL,
|
||||
projectId,
|
||||
inputs
|
||||
) => {
|
||||
const policies = await approvalPolicyDAL.find({
|
||||
type: policyType,
|
||||
projectId
|
||||
});
|
||||
|
||||
let bestMatch: { policy: TPamAccessPolicy; wildcardCount: number; pathLength: number } | null = null;
|
||||
|
||||
for (const policy of policies) {
|
||||
const p = policy as TPamAccessPolicy;
|
||||
for (const c of p.conditions.conditions) {
|
||||
if (!c.targetResources.some((r) => r === inputs.resourceId)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the most specific path pattern
|
||||
// TODO: Make matching logic more advanced by accounting for wildcard positions
|
||||
for (const pathPattern of c.accountPaths) {
|
||||
if (picomatch(pathPattern)(inputs.accountPath)) {
|
||||
const wildcardCount = (pathPattern.match(/\*/g) || []).length;
|
||||
const pathLength = pathPattern.length;
|
||||
|
||||
if (
|
||||
!bestMatch ||
|
||||
wildcardCount < bestMatch.wildcardCount ||
|
||||
(wildcardCount === bestMatch.wildcardCount && pathLength > bestMatch.pathLength)
|
||||
) {
|
||||
bestMatch = { policy: p, wildcardCount, pathLength };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatch?.policy || null;
|
||||
};
|
||||
|
||||
const canAccess: TApprovalRequestFactoryCanAccess<TPamAccessPolicyInputs> = async (
|
||||
approvalRequestGrantsDAL,
|
||||
projectId,
|
||||
userId,
|
||||
inputs
|
||||
) => {
|
||||
const grants = await approvalRequestGrantsDAL.find({
|
||||
granteeUserId: userId,
|
||||
type: policyType,
|
||||
status: ApprovalRequestGrantStatus.Active,
|
||||
projectId,
|
||||
revokedAt: null
|
||||
});
|
||||
|
||||
// TODO: Move some of this check to be part of SQL query
|
||||
return grants.some((grant) => {
|
||||
const grantAttributes = grant.attributes as TPamAccessPolicyInputs;
|
||||
const isMatch = picomatch(grantAttributes.accountPath);
|
||||
return (
|
||||
grantAttributes.resourceId === inputs.resourceId &&
|
||||
isMatch(inputs.accountPath) &&
|
||||
(!grant.expiresAt || grant.expiresAt > new Date())
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
matchPolicy,
|
||||
canAccess
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
BaseApprovalPolicySchema,
|
||||
BaseCreateApprovalPolicySchema,
|
||||
BaseUpdateApprovalPolicySchema
|
||||
} from "../approval-policy-schemas";
|
||||
|
||||
// Inputs
|
||||
export const PamAccessPolicyInputsSchema = z.object({
|
||||
resourceId: z.string().uuid(),
|
||||
accountPath: z.string()
|
||||
});
|
||||
|
||||
// Conditions
|
||||
export const PamAccessPolicyConditionsSchema = z
|
||||
.object({
|
||||
targetResources: z.string().uuid().array(),
|
||||
accountPaths: z.string().array() // TODO: Add path & wildcard validation
|
||||
})
|
||||
.array();
|
||||
|
||||
// Constraints
|
||||
export const PamAccessPolicyConstraintsSchema = z.object({
|
||||
requestDurationHours: z.object({
|
||||
// 168 hours = 7 days
|
||||
min: z.number().min(0).max(168),
|
||||
max: z.number().min(1).max(168)
|
||||
})
|
||||
});
|
||||
|
||||
// Policy
|
||||
export const PamAccessPolicySchema = BaseApprovalPolicySchema.extend({
|
||||
conditions: z.object({
|
||||
version: z.literal(1),
|
||||
conditions: PamAccessPolicyConditionsSchema
|
||||
}),
|
||||
constraints: z.object({
|
||||
version: z.literal(1),
|
||||
constraints: PamAccessPolicyConstraintsSchema
|
||||
})
|
||||
});
|
||||
|
||||
export const CreatePamAccessPolicySchema = BaseCreateApprovalPolicySchema.extend({
|
||||
conditions: PamAccessPolicyConditionsSchema,
|
||||
constraints: PamAccessPolicyConstraintsSchema
|
||||
});
|
||||
|
||||
export const UpdatePamAccessPolicySchema = BaseUpdateApprovalPolicySchema.extend({
|
||||
conditions: PamAccessPolicyConditionsSchema.optional(),
|
||||
constraints: PamAccessPolicyConstraintsSchema.optional()
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
PamAccessPolicyConditionsSchema,
|
||||
PamAccessPolicyConstraintsSchema,
|
||||
PamAccessPolicyInputsSchema,
|
||||
PamAccessPolicySchema
|
||||
} from "./pam-access-policy-schemas";
|
||||
|
||||
export type TPamAccessPolicy = z.infer<typeof PamAccessPolicySchema>;
|
||||
export type TPamAccessPolicyInputs = z.infer<typeof PamAccessPolicyInputsSchema>;
|
||||
export type TPamAccessPolicyConditions = z.infer<typeof PamAccessPolicyConditionsSchema>;
|
||||
export type TPamAccessPolicyConstraints = z.infer<typeof PamAccessPolicyConstraintsSchema>;
|
||||
Reference in New Issue
Block a user