Merge branch 'main' into ENG-2625

This commit is contained in:
x
2025-04-23 17:59:34 -04:00
65 changed files with 545 additions and 434 deletions

View File

@@ -267,7 +267,6 @@ export const secretReplicationServiceFactory = ({
const sourceLocalSecrets = await secretV2BridgeDAL.find({ folderId: folder.id, type: SecretType.Shared });
const sourceSecretImports = await secretImportDAL.find({ folderId: folder.id });
const sourceImportedSecrets = await fnSecretsV2FromImports({
projectId,
secretImports: sourceSecretImports,
secretDAL: secretV2BridgeDAL,
folderDAL,

View File

@@ -1089,7 +1089,8 @@ export const registerRoutes = async (
secretApprovalRequestSecretDAL,
kmsService,
snapshotService,
resourceMetadataDAL
resourceMetadataDAL,
keyStore
});
const secretApprovalRequestService = secretApprovalRequestServiceFactory({

View File

@@ -29,7 +29,8 @@ import { SanitizedProjectSchema } from "../sanitizedSchemas";
const projectWithEnv = SanitizedProjectSchema.extend({
_id: z.string(),
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array()
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array(),
kmsSecretManagerKeyId: z.string().nullable().optional()
});
export const registerProjectRouter = async (server: FastifyZodProvider) => {

View File

@@ -435,12 +435,16 @@ export const identityKubernetesAuthServiceFactory = ({
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId });
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
if (!identityKubernetesAuth) {
throw new NotFoundError({ message: `Failed to find Kubernetes Auth for identity with ID ${identityId}` });
}
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.KUBERNETES_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Kubernetes Auth attached"
});
}
const identityKubernetesAuth = await identityKubernetesAuthDAL.findOne({ identityId });
const { permission } = await permissionService.getOrgPermission(
actor,

View File

@@ -50,7 +50,7 @@ const getIntegrationSecretsV2 = async (
}
// process secrets in current folder
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId: dto.folderId, projectId: dto.projectId });
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId: dto.folderId });
secrets.forEach((secret) => {
const secretKey = secret.key;
@@ -63,7 +63,6 @@ const getIntegrationSecretsV2 = async (
// if no imports then return secrets in the current folder
if (!secretImports.length) return content;
const importedSecrets = await fnSecretsV2FromImports({
projectId: dto.projectId,
decryptor: dto.decryptor,
folderDAL,
secretDAL: secretV2BridgeDAL,

View File

@@ -159,8 +159,7 @@ export const fnSecretsV2FromImports = async ({
decryptor,
expandSecretReferences,
hasSecretAccess,
viewSecretValue,
projectId
viewSecretValue
}: {
secretImports: (Omit<TSecretImports, "importEnv"> & {
importEnv: { id: string; slug: string; name: string };
@@ -177,7 +176,6 @@ export const fnSecretsV2FromImports = async ({
environment: string;
}) => Promise<string | undefined>;
hasSecretAccess: (environment: string, secretPath: string, secretName: string, secretTagSlugs: string[]) => boolean;
projectId: string;
}) => {
const cyclicDetector = new Set();
const stack: {
@@ -218,8 +216,7 @@ export const fnSecretsV2FromImports = async ({
type: SecretType.Shared
},
{
sort: [["id", "asc"]],
useCache: { projectId }
sort: [["id", "asc"]]
}
);
const importedSecretsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId);

View File

@@ -698,7 +698,6 @@ export const secretImportServiceFactory = ({
projectId
});
const importedSecrets = await fnSecretsV2FromImports({
projectId,
secretImports,
folderDAL,
viewSecretValue: true,

View File

@@ -214,7 +214,7 @@ export const secretSyncQueueFactory = ({
canExpandValue: () => true
});
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId, projectId });
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId });
await Promise.allSettled(
secrets.map(async (secret) => {
@@ -244,7 +244,6 @@ export const secretSyncQueueFactory = ({
if (secretImports.length) {
const importedSecrets = await fnSecretsV2FromImports({
projectId,
decryptor: decryptSecretValue,
folderDAL,
secretDAL: secretV2BridgeDAL,

View File

@@ -1,3 +1,4 @@
import { MongoAbility } from "@casl/ability";
import { Knex } from "knex";
import { validate as uuidValidate } from "uuid";
@@ -15,46 +16,29 @@ import {
TFindFilter,
TFindOpt
} from "@app/lib/knex";
import { BufferKeysToString, OrderByDirection } from "@app/lib/types";
import { OrderByDirection } from "@app/lib/types";
import { SecretsOrderBy } from "@app/services/secret/secret-types";
import type { TFindSecretsByFolderIdsFilter } from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
import type {
TFindSecretsByFolderIdsFilter,
TGetSecretsDTO
} from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
export const SecretDalCacheKeys = {
export const SecretServiceCacheKeys = {
get productKey() {
const { INFISICAL_PLATFORM_VERSION } = getConfig();
return `${ProjectType.SecretManager}:${INFISICAL_PLATFORM_VERSION || 0}`;
},
getSecretDalVersion: (projectId: string) => {
return `${SecretDalCacheKeys.productKey}:${projectId}:${TableName.SecretV2}-dal-version`;
return `${SecretServiceCacheKeys.productKey}:${projectId}:${TableName.SecretV2}-dal-version`;
},
findByFolderIds: (
getSecretsOfServiceLayer: (
projectId: string,
version: number,
{ useCache, tx, ...cacheKey }: Parameters<TSecretV2BridgeDALFactory["findByFolderIds"]>[0]
dto: TGetSecretsDTO & { permissionRules: MongoAbility["rules"] }
) => {
return `${SecretDalCacheKeys.productKey}:${projectId}:${
return `${SecretServiceCacheKeys.productKey}:${projectId}:${
TableName.SecretV2
}-dal:v${version}:find-by-folder-ids:${generateCacheKeyFromData(cacheKey)}`;
},
findByFolderId: (
projectId: string,
version: number,
{ useCache, tx, ...cacheKey }: Parameters<TSecretV2BridgeDALFactory["findByFolderId"]>[0]
) => {
return `${SecretDalCacheKeys.productKey}:${projectId}:${
TableName.SecretV2
}-dal:v${version}:find-by-folder-id:${generateCacheKeyFromData(cacheKey)}`;
},
find: (projectId: string, version: number, ...args: Parameters<TSecretV2BridgeDALFactory["find"]>) => {
const [filter, opts] = args;
delete opts?.tx;
delete opts?.useCache;
return `${SecretDalCacheKeys.productKey}:${projectId}:${
TableName.SecretV2
}-dal:v${version}:find:${generateCacheKeyFromData({
filter,
opts
})}`;
}-dal:v${version}:get-secrets-service-layer:${dto.actorId}-${generateCacheKeyFromData(dto)}`;
}
};
@@ -64,14 +48,14 @@ interface TSecretV2DalArg {
keyStore: TKeyStoreFactory;
}
const SECRET_DAL_TTL = 5 * 60;
const SECRET_DAL_VERSION_TTL = 15 * 60;
const MAX_SECRET_CACHE_BYTES = 25 * 1024 * 1024;
export const SECRET_DAL_TTL = 5 * 60;
export const SECRET_DAL_VERSION_TTL = 15 * 60;
export const MAX_SECRET_CACHE_BYTES = 25 * 1024 * 1024;
export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
const secretOrm = ormify(db, TableName.SecretV2);
const invalidateSecretCacheByProjectId = async (projectId: string) => {
const secretDalVersionKey = SecretDalCacheKeys.getSecretDalVersion(projectId);
const secretDalVersionKey = SecretServiceCacheKeys.getSecretDalVersion(projectId);
await keyStore.incrementBy(secretDalVersionKey, 1);
await keyStore.setExpiry(secretDalVersionKey, SECRET_DAL_VERSION_TTL);
};
@@ -128,35 +112,9 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
}
};
const find = async (
filter: TFindFilter<TSecretsV2>,
opts: TFindOpt<TSecretsV2> & { useCache?: { projectId: string } } = {}
) => {
const { offset, limit, sort, tx, useCache } = opts;
const find = async (filter: TFindFilter<TSecretsV2>, opts: TFindOpt<TSecretsV2> = {}) => {
const { offset, limit, sort, tx } = opts;
try {
let secretDalVersion = 0;
if (useCache) {
const cachedSecretDalVersion = await keyStore.getItem(
SecretDalCacheKeys.getSecretDalVersion(useCache.projectId)
);
secretDalVersion = Number(cachedSecretDalVersion || 0);
const cacheKey = SecretDalCacheKeys.find(useCache.projectId, secretDalVersion, filter, opts);
const cachedSecrets = await keyStore.getItem(cacheKey);
if (cachedSecrets) {
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL);
const unsanitizedSecrets = JSON.parse(cachedSecrets) as BufferKeysToString<(typeof data)[number]>[];
const sanitizedSecrets = unsanitizedSecrets.map((el) => {
const encryptedValue = el.encryptedValue ? Buffer.from(el.encryptedValue, "base64") : null;
const encryptedComment = el.encryptedComment ? Buffer.from(el.encryptedComment, "base64") : null;
const createdAt = new Date(el.createdAt);
const updatedAt = new Date(el.updatedAt);
return { ...el, encryptedComment, encryptedValue, createdAt, updatedAt };
});
return sanitizedSecrets;
}
}
const query = (tx || db)(TableName.SecretV2)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
.where(buildFindFilter(filter))
@@ -225,22 +183,6 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
]
});
if (useCache) {
const cachedSecrets = data.map((el) => {
const encryptedValue = el.encryptedValue ? el.encryptedValue.toString("base64") : null;
const encryptedComment = el.encryptedComment ? el.encryptedComment.toString("base64") : null;
return { ...el, encryptedValue, encryptedComment };
});
const cache = JSON.stringify(cachedSecrets);
if (Buffer.byteLength(cache, "utf8") < MAX_SECRET_CACHE_BYTES) {
await keyStore.setItemWithExpiry(
SecretDalCacheKeys.find(useCache.projectId, secretDalVersion, filter, opts),
SECRET_DAL_TTL,
cache
);
}
}
return data;
} catch (error) {
throw new DatabaseError({ error, name: `${TableName.SecretV2}: Find` });
@@ -345,15 +287,9 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
}
};
const findByFolderId = async (dto: {
folderId: string;
userId?: string;
tx?: Knex;
projectId: string;
useCache?: boolean;
}) => {
const findByFolderId = async (dto: { folderId: string; userId?: string; tx?: Knex }) => {
try {
const { folderId, tx, projectId } = dto;
const { folderId, tx } = dto;
let { userId } = dto;
// check if not uui then userId id is null (corner case because service token's ID is not UUI in effort to keep backwards compatibility from mongo
if (userId && !uuidValidate(userId)) {
@@ -361,27 +297,6 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
userId = undefined;
}
const cachedSecretDalVersion = await keyStore.getItem(SecretDalCacheKeys.getSecretDalVersion(projectId));
const secretDalVersion = Number(cachedSecretDalVersion || 0);
if (dto.useCache) {
const cacheKey = SecretDalCacheKeys.findByFolderId(projectId, secretDalVersion, dto);
const cachedSecrets = await keyStore.getItem(cacheKey);
if (cachedSecrets) {
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL);
const unsanitizedSecrets = JSON.parse(cachedSecrets) as BufferKeysToString<(typeof data)[number]>[];
const sanitizedSecrets = unsanitizedSecrets.map((el) => {
const encryptedValue = el.encryptedValue ? Buffer.from(el.encryptedValue, "base64") : null;
const encryptedComment = el.encryptedComment ? Buffer.from(el.encryptedComment, "base64") : null;
const createdAt = new Date(el.createdAt);
const updatedAt = new Date(el.updatedAt);
return { ...el, encryptedComment, encryptedValue, createdAt, updatedAt };
});
return sanitizedSecrets;
}
}
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
.where({ folderId })
.where((bd) => {
@@ -437,22 +352,6 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
}
]
});
if (dto.useCache) {
const newCachedSecrets = data.map((el) => {
const encryptedValue = el.encryptedValue ? el.encryptedValue.toString("base64") : null;
const encryptedComment = el.encryptedComment ? el.encryptedComment.toString("base64") : null;
return { ...el, encryptedValue, encryptedComment };
});
const cache = JSON.stringify(newCachedSecrets);
if (Buffer.byteLength(cache, "utf8") < MAX_SECRET_CACHE_BYTES) {
await keyStore.setItemWithExpiry(
SecretDalCacheKeys.findByFolderId(projectId, secretDalVersion, dto),
SECRET_DAL_TTL,
cache
);
}
}
return data;
} catch (error) {
throw new DatabaseError({ error, name: "get all secret" });
@@ -542,11 +441,9 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
folderIds: string[];
userId?: string;
tx?: Knex;
projectId: string;
filters?: TFindSecretsByFolderIdsFilter;
useCache?: boolean;
}) => {
const { folderIds, tx, filters, useCache, projectId } = dto;
const { folderIds, tx, filters } = dto;
let { userId } = dto;
try {
// check if not uui then userId id is null (corner case because service token's ID is not UUI in effort to keep backwards compatibility from mongo)
@@ -555,26 +452,6 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
userId = undefined;
}
const cachedSecretDalVersion = await keyStore.getItem(SecretDalCacheKeys.getSecretDalVersion(projectId));
const secretDalVersion = Number(cachedSecretDalVersion || 0);
if (useCache) {
const cacheKey = SecretDalCacheKeys.findByFolderIds(projectId, secretDalVersion, dto);
const cachedSecrets = await keyStore.getItem(cacheKey);
if (cachedSecrets) {
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL);
const unsanitizedSecrets = JSON.parse(cachedSecrets) as BufferKeysToString<(typeof data)[number]>[];
const sanitizedSecrets = unsanitizedSecrets.map((el) => {
const encryptedValue = el.encryptedValue ? Buffer.from(el.encryptedValue, "base64") : null;
const encryptedComment = el.encryptedComment ? Buffer.from(el.encryptedComment, "base64") : null;
const createdAt = new Date(el.createdAt);
const updatedAt = new Date(el.updatedAt);
return { ...el, encryptedComment, encryptedValue, createdAt, updatedAt };
});
return sanitizedSecrets;
}
}
const query = (tx || db.replicaNode())(TableName.SecretV2)
.whereIn(`${TableName.SecretV2}.folderId`, folderIds)
.where((bd) => {
@@ -700,22 +577,6 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
}
]
});
if (useCache) {
const cachedSecrets = data.map((el) => {
const encryptedValue = el.encryptedValue ? el.encryptedValue.toString("base64") : null;
const encryptedComment = el.encryptedComment ? el.encryptedComment.toString("base64") : null;
return { ...el, encryptedValue, encryptedComment };
});
const cache = JSON.stringify(cachedSecrets);
if (Buffer.byteLength(cache, "utf8") < MAX_SECRET_CACHE_BYTES) {
await keyStore.setItemWithExpiry(
SecretDalCacheKeys.findByFolderIds(projectId, secretDalVersion, dto),
SECRET_DAL_TTL,
cache
);
}
}
return data;
} catch (error) {

View File

@@ -509,7 +509,7 @@ export const expandSecretReferencesFactory = ({
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder) return { value: "", tags: [] };
const secrets = await secretDAL.findByFolderId({ folderId: folder.id, projectId, useCache: true });
const secrets = await secretDAL.findByFolderId({ folderId: folder.id });
const decryptedSecret = secrets.reduce<Record<string, { value: string; tags: string[] }>>((prev, secret) => {
// eslint-disable-next-line no-param-reassign

View File

@@ -25,6 +25,7 @@ import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-app
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { DatabaseErrorCode } from "@app/lib/error-codes";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { diff, groupBy } from "@app/lib/fn";
@@ -43,7 +44,12 @@ import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import {
MAX_SECRET_CACHE_BYTES,
SECRET_DAL_TTL,
SecretServiceCacheKeys,
TSecretV2BridgeDALFactory
} from "./secret-v2-bridge-dal";
import {
buildHierarchy,
expandSecretReferencesFactory,
@@ -105,6 +111,7 @@ type TSecretV2BridgeServiceFactoryDep = {
>;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
keyStore: Pick<TKeyStoreFactory, "getItem" | "setExpiry" | "setItemWithExpiry" | "deleteItem">;
};
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
@@ -127,7 +134,8 @@ export const secretV2BridgeServiceFactory = ({
secretApprovalRequestDAL,
secretApprovalRequestSecretDAL,
kmsService,
resourceMetadataDAL
resourceMetadataDAL,
keyStore
}: TSecretV2BridgeServiceFactoryDep) => {
const $validateSecretReferences = async (
projectId: string,
@@ -800,12 +808,10 @@ export const secretV2BridgeServiceFactory = ({
const groupedFolderMappings = groupBy(folderMappings, (folderMapping) => folderMapping.folderId);
const secrets = await secretDAL.findByFolderIds({
projectId,
folderIds: folderMappings.map((folderMapping) => folderMapping.folderId),
userId,
tx: undefined,
filters,
useCache: true
filters
});
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
@@ -909,21 +915,22 @@ export const secretV2BridgeServiceFactory = ({
return decryptedSecrets;
};
const getSecrets = async ({
actorId,
path,
environment,
projectId,
actor,
actorOrgId,
viewSecretValue,
actorAuthMethod,
includeImports,
recursive,
expandSecretReferences: shouldExpandSecretReferences,
throwOnMissingReadValuePermission = true,
...params
}: TGetSecretsDTO) => {
const getSecrets = async (dto: TGetSecretsDTO) => {
const {
actorId,
path,
environment,
projectId,
actor,
actorOrgId,
viewSecretValue,
actorAuthMethod,
includeImports,
recursive,
expandSecretReferences: shouldExpandSecretReferences,
throwOnMissingReadValuePermission = true,
...params
} = dto;
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
@@ -934,6 +941,42 @@ export const secretV2BridgeServiceFactory = ({
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
const cachedSecretDalVersion = await keyStore.getItem(SecretServiceCacheKeys.getSecretDalVersion(projectId));
const secretDalVersion = Number(cachedSecretDalVersion || 0);
const cacheKey = SecretServiceCacheKeys.getSecretsOfServiceLayer(projectId, secretDalVersion, {
...dto,
permissionRules: permission.rules
});
const { decryptor: secretManagerDecryptor, encryptor: secretManagerEncryptor } =
await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId
});
const encryptedCachedSecrets = await keyStore.getItem(cacheKey);
if (encryptedCachedSecrets) {
try {
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL);
const cachedSecrets = secretManagerDecryptor({ cipherTextBlob: Buffer.from(encryptedCachedSecrets, "base64") });
const { secrets, imports = [] } = JSON.parse(cachedSecrets.toString("utf8")) as {
secrets: typeof decryptedSecrets;
imports: typeof importedSecrets;
};
return {
secrets: secrets.map((el) => ({
...el,
createdAt: new Date(el.createdAt),
updatedAt: new Date(el.updatedAt)
})),
imports
};
} catch (err) {
logger.error(err, "Secret service layer cache miss");
await keyStore.deleteItem(cacheKey);
}
}
let paths: { folderId: string; path: string }[] = [];
if (recursive) {
@@ -958,17 +1001,10 @@ export const secretV2BridgeServiceFactory = ({
const groupedPaths = groupBy(paths, (p) => p.folderId);
const secrets = await secretDAL.findByFolderIds({
projectId,
folderIds: paths.map((p) => p.folderId),
userId: actorId,
tx: undefined,
filters: params,
useCache: true
});
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId
filters: params
});
// scott: if any of this changes it also needs to be mirrored in secret rotation for getting dashboard secrets
@@ -1086,15 +1122,19 @@ export const secretV2BridgeServiceFactory = ({
}
if (!includeImports) {
return {
secrets: decryptedSecrets
};
const payload = { secrets: decryptedSecrets, imports: [] };
const encryptedUpdatedCachedSecrets = secretManagerEncryptor({
plainText: Buffer.from(JSON.stringify(payload))
}).cipherTextBlob;
if (encryptedUpdatedCachedSecrets.byteLength < MAX_SECRET_CACHE_BYTES) {
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL, encryptedUpdatedCachedSecrets.toString("base64"));
}
return payload;
}
const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId));
const allowedImports = secretImports.filter(({ isReplication }) => !isReplication);
const importedSecrets = await fnSecretsV2FromImports({
projectId,
viewSecretValue,
secretImports: allowedImports,
secretDAL,
@@ -1129,10 +1169,14 @@ export const secretV2BridgeServiceFactory = ({
}
});
return {
secrets: decryptedSecrets,
imports: importedSecrets
};
const payload = { secrets: decryptedSecrets, imports: importedSecrets };
const encryptedUpdatedCachedSecrets = secretManagerEncryptor({
plainText: Buffer.from(JSON.stringify(payload))
}).cipherTextBlob;
if (encryptedUpdatedCachedSecrets.byteLength < MAX_SECRET_CACHE_BYTES) {
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL, encryptedUpdatedCachedSecrets.toString("base64"));
}
return payload;
};
const getSecretById = async ({ actorId, actor, actorOrgId, actorAuthMethod, secretId }: TGetASecretByIdDTO) => {
@@ -1312,7 +1356,6 @@ export const secretV2BridgeServiceFactory = ({
if (!secret && includeImports) {
const secretImports = await secretImportDAL.find({ folderId, isReplication: false });
const importedSecrets = await fnSecretsV2FromImports({
projectId,
secretImports,
viewSecretValue,
secretDAL,
@@ -2729,7 +2772,7 @@ export const secretV2BridgeServiceFactory = ({
generatePaths(folderMap).map(({ folderId, path }) => [folderId, path === "/" ? path : path.substring(1)])
);
const secrets = await secretDAL.findByFolderIds({ folderIds: folders.map((f) => f.id), projectId, useCache: true });
const secrets = await secretDAL.findByFolderIds({ folderIds: folders.map((f) => f.id) });
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,

View File

@@ -367,7 +367,7 @@ export const secretQueueFactory = ({
canExpandValue: () => true
});
// process secrets in current folder
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId: dto.folderId, projectId: dto.projectId });
const secrets = await secretV2BridgeDAL.findByFolderId({ folderId: dto.folderId });
await Promise.allSettled(
secrets.map(async (secret) => {
@@ -397,7 +397,6 @@ export const secretQueueFactory = ({
// if no imports then return secrets in the current folder
if (!secretImports.length) return content;
const importedSecrets = await fnSecretsV2FromImports({
projectId: dto.projectId,
decryptor: dto.decryptor,
folderDAL,
secretDAL: secretV2BridgeDAL,

View File

@@ -28,21 +28,32 @@ You can use it across various environments, whether it's local development, CI/C
```
</Tab>
<Tab title="Windows">
Use [Scoop](https://scoop.sh/) package manager
```bash
scoop bucket add org https://github.com/Infisical/scoop-infisical.git
```
<Accordion title="Scoop package manager">
Use [Scoop](https://scoop.sh/) package manager
```bash
scoop install infisical
```
```bash
scoop bucket add org https://github.com/Infisical/scoop-infisical.git
```
### Updates
```bash
scoop install infisical
```
```bash
scoop update infisical
```
### Updates
```bash
scoop update infisical
```
</Accordion>
<Accordion title="Winget package manager">
Use [Winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/) package manager
```bash
winget install infisical
```
</Accordion>
</Tab>
<Tab title="NPM">

File diff suppressed because one or more lines are too long

View File

@@ -281,6 +281,14 @@ export const ROUTE_PATHS = Object.freeze({
"/cert-manager/$projectId/overview",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview"
),
CertificateAuthoritiesPage: setRoute(
"/cert-manager/$projectId/certificate-authorities",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities"
),
AlertingPage: setRoute(
"/cert-manager/$projectId/alerting",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting"
),
PkiCollectionDetailsByIDPage: setRoute(
"/cert-manager/$projectId/pki-collections/$collectionId",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId"

View File

@@ -92,18 +92,46 @@ export const ProjectLayout = () => {
</Link>
)}
{isCertManager && (
<Link
to={`/${ProjectType.CertificateManager}/$projectId/overview` as const}
params={{
projectId: currentWorkspace.id
}}
>
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="lock-closed">
Overview
</MenuItem>
)}
</Link>
<>
<Link
to={`/${ProjectType.CertificateManager}/$projectId/overview` as const}
params={{
projectId: currentWorkspace.id
}}
>
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="certificate">
Certificates
</MenuItem>
)}
</Link>
<Link
to={
`/${ProjectType.CertificateManager}/$projectId/certificate-authorities` as const
}
params={{
projectId: currentWorkspace.id
}}
>
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="certificate-authority">
Certificate Authorities
</MenuItem>
)}
</Link>
<Link
to={`/${ProjectType.CertificateManager}/$projectId/alerting` as const}
params={{
projectId: currentWorkspace.id
}}
>
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="notification-bell">
Alerting
</MenuItem>
)}
</Link>
</>
)}
{isCmek && (
<Link

View File

@@ -0,0 +1,29 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { ProjectPermissionCan } from "@app/components/permissions";
import { PageHeader } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { PkiAlertsSection } from "./components";
export const AlertingPage = () => {
const { t } = useTranslation();
return (
<div className="container mx-auto flex h-full flex-col justify-between bg-bunker-800 text-white">
<Helmet>
<title>{t("common.head-title", { title: "Alerting" })}</title>
</Helmet>
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader title="Alerting" />
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.PkiAlerts}
>
<PkiAlertsSection />
</ProjectPermissionCan>
</div>
</div>
);
};

View File

@@ -0,0 +1,19 @@
import { createFileRoute } from "@tanstack/react-router";
import { AlertingPage } from "./AlertingPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting"
)({
component: AlertingPage,
beforeLoad: ({ context }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Alerting"
}
]
};
}
});

View File

@@ -20,9 +20,9 @@ import { useDeleteCa, useGetCaById } from "@app/hooks/api";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { CaInstallCertModal } from "../CertificatesPage/components/CaTab/components/CaInstallCertModal";
import { CaModal } from "../CertificatesPage/components/CaTab/components/CaModal";
import { CertificateTemplatesSection } from "../CertificatesPage/components/CertificatesTab/components/CertificateTemplatesSection";
import { CaInstallCertModal } from "../CertificateAuthoritiesPage/components/CaInstallCertModal";
import { CaModal } from "../CertificateAuthoritiesPage/components/CaModal";
import { CertificateTemplatesSection } from "../CertificatesPage/components/CertificateTemplatesSection";
import {
CaCertificatesSection,
CaCrlsSection,

View File

@@ -13,7 +13,7 @@ export const Route = createFileRoute(
{
label: "Certificate Authorities",
link: linkOptions({
to: "/cert-manager/$projectId/overview",
to: "/cert-manager/$projectId/certificate-authorities",
params: {
projectId: params.projectId
}

View File

@@ -0,0 +1,29 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { ProjectPermissionCan } from "@app/components/permissions";
import { PageHeader } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { CaSection } from "./components";
export const CertificateAuthoritiesPage = () => {
const { t } = useTranslation();
return (
<div className="container mx-auto flex h-full flex-col justify-between bg-bunker-800 text-white">
<Helmet>
<title>{t("common.head-title", { title: "Certificate Authorities" })}</title>
</Helmet>
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader title="Certificate Authorities" />
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.CertificateAuthorities}
>
<CaSection />
</ProjectPermissionCan>
</div>
</div>
);
};

View File

@@ -2,7 +2,7 @@ import { Modal, ModalContent } from "@app/components/v2";
import { useGetCaCert } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { CertificateContent } from "../../CertificatesTab/components/CertificateContent";
import { CertificateContent } from "../../CertificatesPage/components/CertificateContent";
type Props = {
popUp: UsePopUpState<["caCert"]>;

View File

@@ -0,0 +1,19 @@
import { createFileRoute } from "@tanstack/react-router";
import { CertificateAuthoritiesPage } from "./CertificateAuthoritiesPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities"
)({
component: CertificateAuthoritiesPage,
beforeLoad: ({ context }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Certificate Authorities"
}
]
};
}
});

View File

@@ -2,63 +2,49 @@ import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { ProjectPermissionCan } from "@app/components/permissions";
import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { PageHeader } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { CaTab, CertificatesTab, PkiAlertsTab } from "./components";
enum TabSections {
Ca = "certificate-authorities",
Certificates = "certificates",
Alerting = "alerting"
}
import { PkiCollectionSection } from "../AlertingPage/components";
import { CertificatesSection } from "./components";
export const CertificatesPage = () => {
const { t } = useTranslation();
const { permission } = useProjectPermission();
const canAccessPkiColl = permission.can(
ProjectPermissionActions.Read,
ProjectPermissionSub.PkiCollections
);
const canAccessCerts = permission.can(
ProjectPermissionActions.Read,
ProjectPermissionSub.Certificates
);
return (
<div className="container mx-auto flex h-full flex-col justify-between bg-bunker-800 text-white">
<Helmet>
<title>{t("common.head-title", { title: "Certificates" })}</title>
</Helmet>
<div className="mx-auto mb-6 w-full max-w-7xl">
<PageHeader title="Overview" />
<Tabs defaultValue={TabSections.Certificates}>
<TabList>
<Tab value={TabSections.Certificates}>Certificates</Tab>
<Tab value={TabSections.Ca}>Certificate Authorities</Tab>
<Tab value={TabSections.Alerting}>Alerting</Tab>
</TabList>
<TabPanel value={TabSections.Certificates}>
<ProjectPermissionCan
renderGuardBanner
passThrough={false}
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.Certificates}
>
<CertificatesTab />
</ProjectPermissionCan>
</TabPanel>
<TabPanel value={TabSections.Ca}>
<ProjectPermissionCan
renderGuardBanner
passThrough={false}
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.CertificateAuthorities}
>
<CaTab />
</ProjectPermissionCan>
</TabPanel>
<TabPanel value={TabSections.Alerting}>
<ProjectPermissionCan
renderGuardBanner
passThrough={false}
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.PkiAlerts}
>
<PkiAlertsTab />
</ProjectPermissionCan>
</TabPanel>
</Tabs>
<PageHeader title="Certificates" />
{/* If both are false, the section does not render. This is to prevent duplicate banners. */}
{(canAccessCerts || canAccessPkiColl) && (
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.PkiCollections}
>
<PkiCollectionSection />
</ProjectPermissionCan>
)}
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.Certificates}
>
<CertificatesSection />
</ProjectPermissionCan>
</div>
</div>
);

View File

@@ -1,17 +0,0 @@
import { motion } from "framer-motion";
import { CaSection } from "./components";
export const CaTab = () => {
return (
<motion.div
key="panel-certificate-authorities"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<CaSection />
</motion.div>
);
};

View File

@@ -1 +0,0 @@
export { CaTab } from "./CaTab";

View File

@@ -1,21 +0,0 @@
import { motion } from "framer-motion";
import { PkiCollectionSection } from "../PkiAlertsTab/components";
// import { CertificateTemplatesSection } from "./components/CertificateTemplatesSection";
import { CertificatesSection } from "./components";
export const CertificatesTab = () => {
return (
<motion.div
key="panel-certificates"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<PkiCollectionSection />
{/* <CertificateTemplatesSection /> */}
<CertificatesSection />
</motion.div>
);
};

View File

@@ -1 +0,0 @@
export { CertificatesSection } from "./CertificatesSection";

View File

@@ -1 +0,0 @@
export { CertificatesTab } from "./CertificatesTab";

View File

@@ -1,17 +0,0 @@
import { motion } from "framer-motion";
import { PkiAlertsSection } from "./components";
export const PkiAlertsTab = () => {
return (
<motion.div
key="panel-alerts"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<PkiAlertsSection />
</motion.div>
);
};

View File

@@ -1 +0,0 @@
export { PkiAlertsTab } from "./PkiAlertsTab";

View File

@@ -1,3 +1 @@
export { CaTab } from "./CaTab";
export { CertificatesTab } from "./CertificatesTab";
export { PkiAlertsTab } from "./PkiAlertsTab";
export { CertificatesSection } from "./CertificatesSection";

View File

@@ -22,7 +22,7 @@ import { PkiItemType } from "@app/hooks/api/pkiCollections/constants";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { PkiCollectionModal } from "../CertificatesPage/components/PkiAlertsTab/components/PkiCollectionModal";
import { PkiCollectionModal } from "../AlertingPage/components/PkiCollectionModal";
import { PkiCollectionDetailsSection, PkiCollectionItemsSection } from "./components";
export const PkiCollectionPage = () => {

View File

@@ -1,6 +1,6 @@
import { Modal, ModalContent } from "@app/components/v2";
import { KmipClientCertificate } from "@app/hooks/api/kmip/types";
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificatesTab/components/CertificateContent";
import { CertificateContent } from "@app/pages/cert-manager/CertificatesPage/components/CertificateContent";
type Props = {
isOpen: boolean;

View File

@@ -0,0 +1,152 @@
import { faAws, faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faCheck, faCopy, faEllipsis } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions/OrgPermissionCan";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "@app/components/v2/Dropdown";
import { IconButton } from "@app/components/v2/IconButton";
import { Td, Tr } from "@app/components/v2/Table";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context/OrgPermissionContext";
import { useToggle } from "@app/hooks";
import { ExternalKmsProvider, KmsListEntry } from "@app/hooks/api/kms/types";
import { SubscriptionPlan } from "@app/hooks/api/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
kms: KmsListEntry;
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["editExternalKms", "removeExternalKms", "upgradePlan"]>,
data?: {
kmsId?: string;
name?: string;
provider?: string;
}
) => void;
subscription: SubscriptionPlan;
};
export const ExternalKmsItem = ({ kms, handlePopUpOpen, subscription }: Props) => {
const [isKmsIdCopied, { timedToggle: toggleKmsIdCopied }] = useToggle(false);
const [isKmsAliasCopied, { timedToggle: toggleKmsAliasCopied }] = useToggle(false);
return (
<Tr key={kms.id}>
<Td className="flex max-w-xs items-center overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
{kms.externalKms.provider === ExternalKmsProvider.Aws && <FontAwesomeIcon icon={faAws} />}
{kms.externalKms.provider === ExternalKmsProvider.Gcp && (
<FontAwesomeIcon icon={faGoogle} />
)}
<div className="ml-2">{kms.externalKms.provider.toUpperCase()}</div>
</Td>
<Td>
<div className="group flex items-center gap-2">
{kms.name}
<IconButton
size="xs"
ariaLabel="copy icon"
colorSchema="secondary"
className="relative rounded-md opacity-0 group-hover:opacity-100"
onClick={() => {
if (isKmsAliasCopied) {
return;
}
navigator.clipboard.writeText(kms.name);
createNotification({
text: "KMS alias copied to clipboard",
type: "success"
});
toggleKmsAliasCopied(2000);
}}
>
<FontAwesomeIcon icon={isKmsAliasCopied ? faCheck : faCopy} />
</IconButton>
</div>
</Td>
<Td>
<div className="group flex items-center gap-2">
{kms.id}
<IconButton
size="xs"
ariaLabel="copy icon"
colorSchema="secondary"
className="relative rounded-md opacity-0 group-hover:opacity-100"
onClick={() => {
if (isKmsIdCopied) {
return;
}
navigator.clipboard.writeText(kms.id);
createNotification({
text: "KMS ID copied to clipboard",
type: "success"
});
toggleKmsIdCopied(2000);
}}
>
<FontAwesomeIcon icon={isKmsIdCopied ? faCheck : faCopy} />
</IconButton>
</div>
</Td>
<Td>
<DropdownMenu>
<DropdownMenuTrigger asChild className="rounded-lg">
<div className="flex justify-end hover:text-primary-400 data-[state=open]:text-primary-400">
<FontAwesomeIcon size="sm" icon={faEllipsis} />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan I={OrgPermissionActions.Edit} an={OrgPermissionSubjects.Kms}>
{(isAllowed) => (
<DropdownMenuItem
disabled={!isAllowed}
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={(e) => {
e.stopPropagation();
if (subscription && !subscription?.externalKms) {
handlePopUpOpen("upgradePlan");
return;
}
handlePopUpOpen("editExternalKms", {
kmsId: kms.id
});
}}
>
Edit
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan I={OrgPermissionActions.Delete} an={OrgPermissionSubjects.Kms}>
{(isAllowed) => (
<DropdownMenuItem
disabled={!isAllowed}
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("removeExternalKms", {
name: kms.name,
kmsId: kms.id,
provider: kms.externalKms.provider
});
}}
>
Delete
</DropdownMenuItem>
)}
</OrgPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Td>
</Tr>
);
};

View File

@@ -1,7 +1,5 @@
import { faAws, faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faEllipsis, faLock, faPlus } from "@fortawesome/free-solid-svg-icons";
import { faLock, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
@@ -9,10 +7,6 @@ import { OrgPermissionCan } from "@app/components/permissions";
import {
Button,
DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
EmptyState,
Table,
TableContainer,
@@ -31,9 +25,9 @@ import {
import { withPermission } from "@app/hoc";
import { usePopUp } from "@app/hooks";
import { useGetExternalKmsList, useRemoveExternalKms } from "@app/hooks/api";
import { ExternalKmsProvider } from "@app/hooks/api/kms/types";
import { AddExternalKmsForm } from "./AddExternalKmsForm";
import { ExternalKmsItem } from "./ExternalKmsItem";
import { UpdateExternalKmsForm } from "./UpdateExternalKmsForm";
export const OrgEncryptionTab = withPermission(
@@ -102,6 +96,7 @@ export const OrgEncryptionTab = withPermission(
<Tr>
<Td>Provider</Td>
<Td>Alias</Td>
<Td>ID</Td>
</Tr>
</THead>
<TBody>
@@ -115,78 +110,12 @@ export const OrgEncryptionTab = withPermission(
)}
{!isExternalKmsListLoading &&
externalKmsList?.map((kms) => (
<Tr key={kms.id}>
<Td className="flex max-w-xs items-center overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
{kms.externalKms.provider === ExternalKmsProvider.Aws && (
<FontAwesomeIcon icon={faAws} />
)}
{kms.externalKms.provider === ExternalKmsProvider.Gcp && (
<FontAwesomeIcon icon={faGoogle} />
)}
<div className="ml-2">{kms.externalKms.provider.toUpperCase()}</div>
</Td>
<Td>{kms.name}</Td>
<Td>
<DropdownMenu>
<DropdownMenuTrigger asChild className="rounded-lg">
<div className="flex justify-end hover:text-primary-400 data-[state=open]:text-primary-400">
<FontAwesomeIcon size="sm" icon={faEllipsis} />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan
I={OrgPermissionActions.Edit}
an={OrgPermissionSubjects.Kms}
>
{(isAllowed) => (
<DropdownMenuItem
disabled={!isAllowed}
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={(e) => {
e.stopPropagation();
if (subscription && !subscription?.externalKms) {
handlePopUpOpen("upgradePlan");
return;
}
handlePopUpOpen("editExternalKms", {
kmsId: kms.id
});
}}
>
Edit
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan
I={OrgPermissionActions.Delete}
an={OrgPermissionSubjects.Kms}
>
{(isAllowed) => (
<DropdownMenuItem
disabled={!isAllowed}
className={twMerge(
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
)}
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("removeExternalKms", {
name: kms.name,
kmsId: kms.id,
provider: kms.externalKms.provider
});
}}
>
Delete
</DropdownMenuItem>
)}
</OrgPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Td>
</Tr>
<ExternalKmsItem
key={kms.id}
kms={kms}
handlePopUpOpen={handlePopUpOpen}
subscription={subscription}
/>
))}
</TBody>
</Table>

View File

@@ -92,6 +92,8 @@ import { Route as kmsOverviewPageRouteImport } from './pages/kms/OverviewPage/ro
import { Route as kmsKmipPageRouteImport } from './pages/kms/KmipPage/route'
import { Route as certManagerSettingsPageRouteImport } from './pages/cert-manager/SettingsPage/route'
import { Route as certManagerCertificatesPageRouteImport } from './pages/cert-manager/CertificatesPage/route'
import { Route as certManagerCertificateAuthoritiesPageRouteImport } from './pages/cert-manager/CertificateAuthoritiesPage/route'
import { Route as certManagerAlertingPageRouteImport } from './pages/cert-manager/AlertingPage/route'
import { Route as projectRoleDetailsBySlugPageRouteSshImport } from './pages/project/RoleDetailsBySlugPage/route-ssh'
import { Route as projectMemberDetailsByIDPageRouteSshImport } from './pages/project/MemberDetailsByIDPage/route-ssh'
import { Route as projectIdentityDetailsByIDPageRouteSshImport } from './pages/project/IdentityDetailsByIDPage/route-ssh'
@@ -894,6 +896,20 @@ const certManagerCertificatesPageRouteRoute =
getParentRoute: () => certManagerLayoutRoute,
} as any)
const certManagerCertificateAuthoritiesPageRouteRoute =
certManagerCertificateAuthoritiesPageRouteImport.update({
id: '/certificate-authorities',
path: '/certificate-authorities',
getParentRoute: () => certManagerLayoutRoute,
} as any)
const certManagerAlertingPageRouteRoute =
certManagerAlertingPageRouteImport.update({
id: '/alerting',
path: '/alerting',
getParentRoute: () => certManagerLayoutRoute,
} as any)
const projectRoleDetailsBySlugPageRouteSshRoute =
projectRoleDetailsBySlugPageRouteSshImport.update({
id: '/roles/$roleSlug',
@@ -2104,6 +2120,20 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof sshLayoutImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSshProjectIdImport
}
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting': {
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting'
path: '/alerting'
fullPath: '/cert-manager/$projectId/alerting'
preLoaderRoute: typeof certManagerAlertingPageRouteImport
parentRoute: typeof certManagerLayoutImport
}
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities': {
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities'
path: '/certificate-authorities'
fullPath: '/cert-manager/$projectId/certificate-authorities'
preLoaderRoute: typeof certManagerCertificateAuthoritiesPageRouteImport
parentRoute: typeof certManagerLayoutImport
}
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview': {
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview'
path: '/overview'
@@ -3119,6 +3149,8 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren =
)
interface certManagerLayoutRouteChildren {
certManagerAlertingPageRouteRoute: typeof certManagerAlertingPageRouteRoute
certManagerCertificateAuthoritiesPageRouteRoute: typeof certManagerCertificateAuthoritiesPageRouteRoute
certManagerCertificatesPageRouteRoute: typeof certManagerCertificatesPageRouteRoute
certManagerSettingsPageRouteRoute: typeof certManagerSettingsPageRouteRoute
projectAccessControlPageRouteCertManagerRoute: typeof projectAccessControlPageRouteCertManagerRoute
@@ -3130,6 +3162,9 @@ interface certManagerLayoutRouteChildren {
}
const certManagerLayoutRouteChildren: certManagerLayoutRouteChildren = {
certManagerAlertingPageRouteRoute: certManagerAlertingPageRouteRoute,
certManagerCertificateAuthoritiesPageRouteRoute:
certManagerCertificateAuthoritiesPageRouteRoute,
certManagerCertificatesPageRouteRoute: certManagerCertificatesPageRouteRoute,
certManagerSettingsPageRouteRoute: certManagerSettingsPageRouteRoute,
projectAccessControlPageRouteCertManagerRoute:
@@ -3794,6 +3829,8 @@ export interface FileRoutesByFullPath {
'/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
'/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute
'/kms/$projectId/kmip': typeof kmsKmipPageRouteRoute
@@ -3969,6 +4006,8 @@ export interface FileRoutesByTo {
'/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
'/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute
'/kms/$projectId/kmip': typeof kmsKmipPageRouteRoute
@@ -4160,6 +4199,8 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout': typeof kmsLayoutRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout': typeof secretManagerLayoutRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout': typeof sshLayoutRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting': typeof certManagerAlertingPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview': typeof certManagerCertificatesPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/settings': typeof certManagerSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/kmip': typeof kmsKmipPageRouteRoute
@@ -4344,6 +4385,8 @@ export interface FileRouteTypes {
| '/organization/secret-manager/overview'
| '/organization/secret-sharing/settings'
| '/organization/ssh/overview'
| '/cert-manager/$projectId/alerting'
| '/cert-manager/$projectId/certificate-authorities'
| '/cert-manager/$projectId/overview'
| '/cert-manager/$projectId/settings'
| '/kms/$projectId/kmip'
@@ -4518,6 +4561,8 @@ export interface FileRouteTypes {
| '/organization/secret-manager/overview'
| '/organization/secret-sharing/settings'
| '/organization/ssh/overview'
| '/cert-manager/$projectId/alerting'
| '/cert-manager/$projectId/certificate-authorities'
| '/cert-manager/$projectId/overview'
| '/cert-manager/$projectId/settings'
| '/kms/$projectId/kmip'
@@ -4707,6 +4752,8 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout'
| '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/settings'
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout/kmip'
@@ -5222,6 +5269,8 @@ export const routeTree = rootRoute
"filePath": "cert-manager/layout.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId",
"children": [
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/settings",
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/access-management",
@@ -5277,6 +5326,14 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/roles/$roleSlug"
]
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/alerting": {
"filePath": "cert-manager/AlertingPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout"
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-authorities": {
"filePath": "cert-manager/CertificateAuthoritiesPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout"
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/overview": {
"filePath": "cert-manager/CertificatesPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout"

View File

@@ -282,6 +282,8 @@ const secretManagerIntegrationsRedirect = route("/integrations", [
const certManagerRoutes = route("/cert-manager/$projectId", [
layout("cert-manager-layout", "cert-manager/layout.tsx", [
route("/overview", "cert-manager/CertificatesPage/route.tsx"),
route("/certificate-authorities", "cert-manager/CertificateAuthoritiesPage/route.tsx"),
route("/alerting", "cert-manager/AlertingPage/route.tsx"),
route("/ca/$caId", "cert-manager/CertAuthDetailsByIDPage/route.tsx"),
route("/pki-collections/$collectionId", "cert-manager/PkiCollectionDetailsByIDPage/routes.tsx"),
route("/settings", "cert-manager/SettingsPage/route.tsx"),