mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
feat: backend changes for assume permission in aws dynamic secret
This commit is contained in:
@@ -99,7 +99,9 @@ export const dynamicSecretLeaseQueueServiceFactory = ({
|
|||||||
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
||||||
) as object;
|
) as object;
|
||||||
|
|
||||||
await selectedProvider.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId);
|
await selectedProvider.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId, {
|
||||||
|
projectId: folder.projectId
|
||||||
|
});
|
||||||
await dynamicSecretLeaseDAL.deleteById(dynamicSecretLease.id);
|
await dynamicSecretLeaseDAL.deleteById(dynamicSecretLease.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -133,7 +135,9 @@ export const dynamicSecretLeaseQueueServiceFactory = ({
|
|||||||
await Promise.all(dynamicSecretLeases.map(({ id }) => unsetLeaseRevocation(id)));
|
await Promise.all(dynamicSecretLeases.map(({ id }) => unsetLeaseRevocation(id)));
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
dynamicSecretLeases.map(({ externalEntityId }) =>
|
dynamicSecretLeases.map(({ externalEntityId }) =>
|
||||||
selectedProvider.revoke(decryptedStoredInput, externalEntityId)
|
selectedProvider.revoke(decryptedStoredInput, externalEntityId, {
|
||||||
|
projectId: folder.projectId
|
||||||
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
result = await selectedProvider.create({
|
result = await selectedProvider.create({
|
||||||
inputs: decryptedStoredInput,
|
inputs: decryptedStoredInput,
|
||||||
expireAt: expireAt.getTime(),
|
expireAt: expireAt.getTime(),
|
||||||
usernameTemplate: dynamicSecretCfg.usernameTemplate
|
usernameTemplate: dynamicSecretCfg.usernameTemplate,
|
||||||
|
metadata: { projectId }
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
|
||||||
@@ -237,7 +238,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
const { entityId } = await selectedProvider.renew(
|
const { entityId } = await selectedProvider.renew(
|
||||||
decryptedStoredInput,
|
decryptedStoredInput,
|
||||||
dynamicSecretLease.externalEntityId,
|
dynamicSecretLease.externalEntityId,
|
||||||
expireAt.getTime()
|
expireAt.getTime(),
|
||||||
|
{ projectId }
|
||||||
);
|
);
|
||||||
|
|
||||||
await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id);
|
await dynamicSecretQueueService.unsetLeaseRevocation(dynamicSecretLease.id);
|
||||||
@@ -313,7 +315,7 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
) as object;
|
) as object;
|
||||||
|
|
||||||
const revokeResponse = await selectedProvider
|
const revokeResponse = await selectedProvider
|
||||||
.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId)
|
.revoke(decryptedStoredInput, dynamicSecretLease.externalEntityId, { projectId })
|
||||||
.catch(async (err) => {
|
.catch(async (err) => {
|
||||||
// only propogate this error if forced is false
|
// only propogate this error if forced is false
|
||||||
if (!isForced) return { error: err as Error };
|
if (!isForced) return { error: err as Error };
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
|
throw new BadRequestError({ message: "Provided dynamic secret already exist under the folder" });
|
||||||
|
|
||||||
const selectedProvider = dynamicSecretProviders[provider.type];
|
const selectedProvider = dynamicSecretProviders[provider.type];
|
||||||
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
|
const inputs = await selectedProvider.validateProviderInputs(provider.inputs, { projectId });
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (inputs && typeof inputs === "object" && "gatewayId" in inputs && inputs.gatewayId) {
|
if (inputs && typeof inputs === "object" && "gatewayId" in inputs && inputs.gatewayId) {
|
||||||
@@ -146,7 +146,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
selectedGatewayId = gateway.id;
|
selectedGatewayId = gateway.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(provider.inputs);
|
const isConnected = await selectedProvider.validateConnection(provider.inputs, { projectId });
|
||||||
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
|
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
|
||||||
|
|
||||||
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
@@ -272,7 +272,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
||||||
) as object;
|
) as object;
|
||||||
const newInput = { ...decryptedStoredInput, ...(inputs || {}) };
|
const newInput = { ...decryptedStoredInput, ...(inputs || {}) };
|
||||||
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
|
const updatedInput = await selectedProvider.validateProviderInputs(newInput, { projectId });
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
||||||
@@ -301,7 +301,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
selectedGatewayId = gateway.id;
|
selectedGatewayId = gateway.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(newInput);
|
const isConnected = await selectedProvider.validateConnection(newInput, { projectId });
|
||||||
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
|
if (!isConnected) throw new BadRequestError({ message: "Provider connection failed" });
|
||||||
|
|
||||||
const updatedDynamicCfg = await dynamicSecretDAL.transaction(async (tx) => {
|
const updatedDynamicCfg = await dynamicSecretDAL.transaction(async (tx) => {
|
||||||
@@ -472,7 +472,9 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
secretManagerDecryptor({ cipherTextBlob: dynamicSecretCfg.encryptedInput }).toString()
|
||||||
) as object;
|
) as object;
|
||||||
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
|
const selectedProvider = dynamicSecretProviders[dynamicSecretCfg.type as DynamicSecretProviders];
|
||||||
const providerInputs = (await selectedProvider.validateProviderInputs(decryptedStoredInput)) as object;
|
const providerInputs = (await selectedProvider.validateProviderInputs(decryptedStoredInput, {
|
||||||
|
projectId
|
||||||
|
})) as object;
|
||||||
|
|
||||||
return { ...dynamicSecretCfg, inputs: providerInputs };
|
return { ...dynamicSecretCfg, inputs: providerInputs };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,13 +16,16 @@ import {
|
|||||||
PutUserPolicyCommand,
|
PutUserPolicyCommand,
|
||||||
RemoveUserFromGroupCommand
|
RemoveUserFromGroupCommand
|
||||||
} from "@aws-sdk/client-iam";
|
} from "@aws-sdk/client-iam";
|
||||||
|
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
import { AwsIamAuthType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
const generateUsername = (usernameTemplate?: string | null) => {
|
const generateUsername = (usernameTemplate?: string | null) => {
|
||||||
const randomUsername = alphaNumericNanoId(32);
|
const randomUsername = alphaNumericNanoId(32);
|
||||||
@@ -40,7 +43,45 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
return providerInputs;
|
return providerInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretAwsIamSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretAwsIamSchema>, projectId: string) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
if (providerInputs.method === AwsIamAuthType.AssumeRole) {
|
||||||
|
console.log(appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID, appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY);
|
||||||
|
const stsClient = new STSClient({
|
||||||
|
region: providerInputs.region,
|
||||||
|
credentials:
|
||||||
|
appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID && appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY
|
||||||
|
? {
|
||||||
|
accessKeyId: appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY
|
||||||
|
}
|
||||||
|
: undefined // if hosting on AWS
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = new AssumeRoleCommand({
|
||||||
|
RoleArn: providerInputs.roleArn,
|
||||||
|
RoleSessionName: `infisical-dynamic-secret-${randomUUID()}`,
|
||||||
|
DurationSeconds: 900, // 15 mins
|
||||||
|
ExternalId: projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
const assumeRes = await stsClient.send(command);
|
||||||
|
|
||||||
|
if (!assumeRes.Credentials?.AccessKeyId || !assumeRes.Credentials?.SecretAccessKey) {
|
||||||
|
throw new BadRequestError({ message: "Failed to assume role - verify credentials and role configuration" });
|
||||||
|
}
|
||||||
|
console.log(assumeRes.Credentials);
|
||||||
|
const client = new IAMClient({
|
||||||
|
region: providerInputs.region,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: assumeRes.Credentials?.AccessKeyId,
|
||||||
|
secretAccessKey: assumeRes.Credentials?.SecretAccessKey,
|
||||||
|
sessionToken: assumeRes.Credentials?.SessionToken
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
const client = new IAMClient({
|
const client = new IAMClient({
|
||||||
region: providerInputs.region,
|
region: providerInputs.region,
|
||||||
credentials: {
|
credentials: {
|
||||||
@@ -52,22 +93,39 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateConnection = async (inputs: unknown) => {
|
const validateConnection = async (inputs: unknown, { projectId }: { projectId: string }) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs, projectId);
|
||||||
|
const isConnected = await client
|
||||||
const isConnected = await client.send(new GetUserCommand({})).then(() => true);
|
.send(new GetUserCommand({}))
|
||||||
|
.then(() => true)
|
||||||
|
.catch((err) => {
|
||||||
|
const message = (err as Error)?.message;
|
||||||
|
if (
|
||||||
|
providerInputs.method === AwsIamAuthType.AssumeRole &&
|
||||||
|
message.includes("Must specify userName when calling with non-User credentials")
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
return isConnected;
|
return isConnected;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
|
const create = async (data: {
|
||||||
const { inputs, usernameTemplate } = data;
|
inputs: unknown;
|
||||||
|
expireAt: number;
|
||||||
|
usernameTemplate?: string | null;
|
||||||
|
metadata: { projectId: string };
|
||||||
|
}) => {
|
||||||
|
const { inputs, usernameTemplate, metadata } = data;
|
||||||
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs, metadata.projectId);
|
||||||
|
|
||||||
const username = generateUsername(usernameTemplate);
|
const username = generateUsername(usernameTemplate);
|
||||||
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
||||||
|
console.log("Hit");
|
||||||
const createUserRes = await client.send(
|
const createUserRes = await client.send(
|
||||||
new CreateUserCommand({
|
new CreateUserCommand({
|
||||||
Path: awsPath,
|
Path: awsPath,
|
||||||
@@ -76,6 +134,8 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
UserName: username
|
UserName: username
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log("Hit fail");
|
||||||
if (!createUserRes.User) throw new BadRequestError({ message: "Failed to create AWS IAM User" });
|
if (!createUserRes.User) throw new BadRequestError({ message: "Failed to create AWS IAM User" });
|
||||||
if (userGroups) {
|
if (userGroups) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@@ -125,9 +185,9 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const revoke = async (inputs: unknown, entityId: string) => {
|
const revoke = async (inputs: unknown, entityId: string, metadata: { projectId: string }) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs, metadata.projectId);
|
||||||
|
|
||||||
const username = entityId;
|
const username = entityId;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ export enum SqlProviders {
|
|||||||
Vertica = "vertica"
|
Vertica = "vertica"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AwsIamAuthType {
|
||||||
|
AssumeRole = "assume-role",
|
||||||
|
AccessKey = "access-key"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ElasticSearchAuthTypes {
|
export enum ElasticSearchAuthTypes {
|
||||||
User = "user",
|
User = "user",
|
||||||
ApiKey = "api-key"
|
ApiKey = "api-key"
|
||||||
@@ -168,16 +173,38 @@ export const DynamicSecretSapAseSchema = z.object({
|
|||||||
revocationStatement: z.string().trim()
|
revocationStatement: z.string().trim()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DynamicSecretAwsIamSchema = z.object({
|
export const DynamicSecretAwsIamSchema = z.preprocess(
|
||||||
accessKey: z.string().trim().min(1),
|
(val) => {
|
||||||
secretAccessKey: z.string().trim().min(1),
|
if (typeof val === "object" && val !== null && !Object.hasOwn(val, "method")) {
|
||||||
region: z.string().trim().min(1),
|
// eslint-disable-next-line no-param-reassign
|
||||||
awsPath: z.string().trim().optional(),
|
(val as { method: string }).method = AwsIamAuthType.AccessKey;
|
||||||
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
}
|
||||||
policyDocument: z.string().trim().optional(),
|
return val;
|
||||||
userGroups: z.string().trim().optional(),
|
},
|
||||||
policyArns: z.string().trim().optional()
|
z.discriminatedUnion("method", [
|
||||||
});
|
z.object({
|
||||||
|
method: z.literal(AwsIamAuthType.AccessKey),
|
||||||
|
accessKey: z.string().trim().min(1),
|
||||||
|
secretAccessKey: z.string().trim().min(1),
|
||||||
|
region: z.string().trim().min(1),
|
||||||
|
awsPath: z.string().trim().optional(),
|
||||||
|
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
||||||
|
policyDocument: z.string().trim().optional(),
|
||||||
|
userGroups: z.string().trim().optional(),
|
||||||
|
policyArns: z.string().trim().optional()
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal(AwsIamAuthType.AssumeRole),
|
||||||
|
roleArn: z.string().trim().min(1, "Role ARN required"),
|
||||||
|
region: z.string().trim().min(1),
|
||||||
|
awsPath: z.string().trim().optional(),
|
||||||
|
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
||||||
|
policyDocument: z.string().trim().optional(),
|
||||||
|
userGroups: z.string().trim().optional(),
|
||||||
|
policyArns: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
export const DynamicSecretMongoAtlasSchema = z.object({
|
export const DynamicSecretMongoAtlasSchema = z.object({
|
||||||
adminPublicKey: z.string().trim().min(1).describe("Admin user public api key"),
|
adminPublicKey: z.string().trim().min(1).describe("Admin user public api key"),
|
||||||
@@ -400,9 +427,15 @@ export type TDynamicProviderFns = {
|
|||||||
inputs: unknown;
|
inputs: unknown;
|
||||||
expireAt: number;
|
expireAt: number;
|
||||||
usernameTemplate?: string | null;
|
usernameTemplate?: string | null;
|
||||||
|
metadata: { projectId: string };
|
||||||
}) => Promise<{ entityId: string; data: unknown }>;
|
}) => Promise<{ entityId: string; data: unknown }>;
|
||||||
validateConnection: (inputs: unknown) => Promise<boolean>;
|
validateConnection: (inputs: unknown, metadata: { projectId: string }) => Promise<boolean>;
|
||||||
validateProviderInputs: (inputs: object) => Promise<unknown>;
|
validateProviderInputs: (inputs: object, metadata: { projectId: string }) => Promise<unknown>;
|
||||||
revoke: (inputs: unknown, entityId: string) => Promise<{ entityId: string }>;
|
revoke: (inputs: unknown, entityId: string, metadata: { projectId: string }) => Promise<{ entityId: string }>;
|
||||||
renew: (inputs: unknown, entityId: string, expireAt: number) => Promise<{ entityId: string }>;
|
renew: (
|
||||||
|
inputs: unknown,
|
||||||
|
entityId: string,
|
||||||
|
expireAt: number,
|
||||||
|
metadata: { projectId: string }
|
||||||
|
) => Promise<{ entityId: string }>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
environmentsUsed: 0,
|
environmentsUsed: 0,
|
||||||
identityLimit: null,
|
identityLimit: null,
|
||||||
identitiesUsed: 0,
|
identitiesUsed: 0,
|
||||||
dynamicSecret: false,
|
dynamicSecret: true,
|
||||||
secretVersioning: true,
|
secretVersioning: true,
|
||||||
pitRecovery: false,
|
pitRecovery: false,
|
||||||
ipAllowlisting: false,
|
ipAllowlisting: false,
|
||||||
|
|||||||
@@ -376,7 +376,8 @@ const DynamicSecretConditionV2Schema = z
|
|||||||
.object({
|
.object({
|
||||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN]
|
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||||
|
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
]),
|
]),
|
||||||
@@ -404,6 +405,23 @@ const DynamicSecretConditionV2Schema = z
|
|||||||
})
|
})
|
||||||
.partial();
|
.partial();
|
||||||
|
|
||||||
|
const SecretImportConditionSchema = z
|
||||||
|
.object({
|
||||||
|
environment: z.union([
|
||||||
|
z.string(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||||
|
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||||
|
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||||
|
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
]),
|
||||||
|
secretPath: SECRET_PATH_PERMISSION_OPERATOR_SCHEMA
|
||||||
|
})
|
||||||
|
.partial();
|
||||||
|
|
||||||
const SecretConditionV2Schema = z
|
const SecretConditionV2Schema = z
|
||||||
.object({
|
.object({
|
||||||
environment: z.union([
|
environment: z.union([
|
||||||
@@ -741,7 +759,7 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
|
|||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
|
||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
),
|
),
|
||||||
conditions: SecretConditionV1Schema.describe(
|
conditions: SecretImportConditionSchema.describe(
|
||||||
"When specified, only matching conditions will be allowed to access given resource."
|
"When specified, only matching conditions will be allowed to access given resource."
|
||||||
).optional()
|
).optional()
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -213,6 +213,12 @@ const envSchema = z
|
|||||||
GATEWAY_RELAY_AUTH_SECRET: zpStr(z.string().optional()),
|
GATEWAY_RELAY_AUTH_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
DYNAMIC_SECRET_ALLOW_INTERNAL_IP: zodStrBool.default("false"),
|
DYNAMIC_SECRET_ALLOW_INTERNAL_IP: zodStrBool.default("false"),
|
||||||
|
DYNAMIC_SECRET_AWS_ACCESS_KEY_ID: zpStr(z.string().optional()).default(
|
||||||
|
process.env.INF_APP_CONNECTION_AWS_ACCESS_KEY_ID
|
||||||
|
),
|
||||||
|
DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY: zpStr(z.string().optional()).default(
|
||||||
|
process.env.INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY
|
||||||
|
),
|
||||||
/* ----------------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* App Connections ----------------------------------------------------------------------------- */
|
/* App Connections ----------------------------------------------------------------------------- */
|
||||||
|
|||||||
Reference in New Issue
Block a user