mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-31 10:38:12 +00:00
Compare commits
32 Commits
daniel/vau
...
daniel/fip
Author | SHA1 | Date | |
---|---|---|---|
|
d4f0301104 | ||
|
253c46f21d | ||
|
11ca76ccca | ||
|
418aca8af0 | ||
|
929822514e | ||
|
616ccb97f2 | ||
|
7917a767e6 | ||
|
ccff675e0d | ||
|
ad905b2ff7 | ||
|
2ada753527 | ||
|
c031736701 | ||
|
91a1c34637 | ||
|
eadb1a63fa | ||
|
f70a1e3db6 | ||
|
fc6ab94a06 | ||
|
4feb3314e7 | ||
|
d9a57d1391 | ||
|
2c99d41592 | ||
|
2535d1bc4b | ||
|
83e59ae160 | ||
|
a8a1bc5f4a | ||
|
3483f185a8 | ||
|
9bc24487b3 | ||
|
cb700c5124 | ||
|
8e829bdf85 | ||
|
dd008724fb | ||
|
dd0c07fb95 | ||
|
d935b28925 | ||
|
60620840f2 | ||
|
e798eb2a4e | ||
|
e96e7b835d | ||
|
e76e0f7bcc |
@@ -145,7 +145,11 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||
&& cd openssl-3.1.2 \
|
||||
&& ./Configure enable-fips \
|
||||
&& make \
|
||||
&& make install_fips
|
||||
&& make install_fips \
|
||||
&& cd / \
|
||||
&& rm -rf /openssl-build \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
# Install Infisical CLI
|
||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||
@@ -186,12 +190,11 @@ ENV NODE_ENV production
|
||||
ENV STANDALONE_BUILD true
|
||||
ENV STANDALONE_MODE true
|
||||
ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/
|
||||
ENV NODE_OPTIONS="--max-old-space-size=1024"
|
||||
ENV NODE_OPTIONS="--max-old-space-size=8192 --force-fips"
|
||||
|
||||
# FIPS mode of operation:
|
||||
ENV OPENSSL_CONF=/backend/nodejs.fips.cnf
|
||||
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
||||
ENV NODE_OPTIONS=--force-fips
|
||||
ENV FIPS_ENABLED=true
|
||||
|
||||
|
||||
|
@@ -59,7 +59,11 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||
&& cd openssl-3.1.2 \
|
||||
&& ./Configure enable-fips \
|
||||
&& make \
|
||||
&& make install_fips
|
||||
&& make install_fips \
|
||||
&& cd / \
|
||||
&& rm -rf /openssl-build \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
# ? App setup
|
||||
|
||||
|
9
backend/src/@types/fastify.d.ts
vendored
9
backend/src/@types/fastify.d.ts
vendored
@@ -126,6 +126,15 @@ declare module "@fastify/request-context" {
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
aws?: {
|
||||
accountId: string;
|
||||
arn: string;
|
||||
userId: string;
|
||||
partition: string;
|
||||
service: string;
|
||||
resourceType: string;
|
||||
resourceName: string;
|
||||
};
|
||||
};
|
||||
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||
assumedPrivilegeDetails?: { requesterId: string; actorId: string; actorType: ActorType; projectId: string };
|
||||
|
@@ -53,7 +53,7 @@ export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory
|
||||
|
||||
let envCfg = Object.freeze(parsedEnv.data);
|
||||
|
||||
const fipsEnabled = await crypto.initialize(superAdminDAL);
|
||||
const fipsEnabled = await crypto.initialize(superAdminDAL, envCfg);
|
||||
|
||||
// Fix for 128-bit entropy encryption key expansion issue:
|
||||
// In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY.
|
||||
|
@@ -2373,6 +2373,10 @@ export const SecretSyncs = {
|
||||
keyId: "The AWS KMS key ID or alias to use when encrypting parameters synced by Infisical.",
|
||||
tags: "Optional tags to add to secrets synced by Infisical.",
|
||||
syncSecretMetadataAsTags: `Whether Infisical secret metadata should be added as tags to secrets synced by Infisical.`
|
||||
},
|
||||
RENDER: {
|
||||
autoRedeployServices:
|
||||
"Whether Infisical should automatically redeploy the configured Render service upon secret changes."
|
||||
}
|
||||
},
|
||||
DESTINATION_CONFIG: {
|
||||
|
@@ -14,7 +14,7 @@ import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal
|
||||
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
import { isBase64 } from "../../base64";
|
||||
import { getConfig } from "../../config/env";
|
||||
import { getConfig, TEnvConfig } from "../../config/env";
|
||||
import { CryptographyError } from "../../errors";
|
||||
import { logger } from "../../logger";
|
||||
import { asymmetricFipsValidated } from "./asymmetric-fips";
|
||||
@@ -106,12 +106,12 @@ const cryptographyFactory = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const $setFipsModeEnabled = (enabled: boolean) => {
|
||||
const $setFipsModeEnabled = (enabled: boolean, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
||||
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
||||
if (enabled) {
|
||||
crypto.setFips(true);
|
||||
|
||||
const appCfg = getConfig();
|
||||
const appCfg = envCfg || getConfig();
|
||||
|
||||
if (appCfg.ENCRYPTION_KEY) {
|
||||
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
||||
@@ -141,14 +141,14 @@ const cryptographyFactory = () => {
|
||||
$isInitialized = true;
|
||||
};
|
||||
|
||||
const initialize = async (superAdminDAL: TSuperAdminDALFactory) => {
|
||||
const initialize = async (superAdminDAL: TSuperAdminDALFactory, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
||||
if ($isInitialized) {
|
||||
return isFipsModeEnabled();
|
||||
}
|
||||
|
||||
if (process.env.FIPS_ENABLED !== "true") {
|
||||
logger.info("Cryptography module initialized in normal operation mode.");
|
||||
$setFipsModeEnabled(false);
|
||||
$setFipsModeEnabled(false, envCfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -158,11 +158,11 @@ const cryptographyFactory = () => {
|
||||
if (serverCfg) {
|
||||
if (serverCfg.fipsEnabled) {
|
||||
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
||||
$setFipsModeEnabled(true);
|
||||
$setFipsModeEnabled(true, envCfg);
|
||||
return true;
|
||||
}
|
||||
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
||||
$setFipsModeEnabled(false);
|
||||
$setFipsModeEnabled(false, envCfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ const cryptographyFactory = () => {
|
||||
// TODO(daniel): check if it's an enterprise deployment
|
||||
|
||||
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
|
||||
$setFipsModeEnabled(true);
|
||||
$setFipsModeEnabled(true, envCfg);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@@ -162,6 +162,12 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
||||
kubernetes: token?.identityAuth?.kubernetes
|
||||
});
|
||||
}
|
||||
if (token?.identityAuth?.aws) {
|
||||
requestContext.set("identityAuthInfo", {
|
||||
identityId: identity.identityId,
|
||||
aws: token?.identityAuth?.aws
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthMode.SERVICE_TOKEN: {
|
||||
|
@@ -270,11 +270,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
remainingLimit -= imports.length;
|
||||
adjustedOffset = 0;
|
||||
} else {
|
||||
adjustedOffset = Math.max(0, adjustedOffset - totalImportCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +312,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!includeDynamicSecrets && !includeSecrets)
|
||||
if (!includeDynamicSecrets && !includeSecrets && !includeSecretRotations)
|
||||
return {
|
||||
folders,
|
||||
totalFolderCount,
|
||||
@@ -547,7 +542,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
(totalFolderCount ?? 0) +
|
||||
(totalDynamicSecretCount ?? 0) +
|
||||
(totalSecretCount ?? 0) +
|
||||
(totalImportCount ?? 0) +
|
||||
(totalSecretRotationCount ?? 0)
|
||||
};
|
||||
}
|
||||
|
@@ -164,7 +164,7 @@ export const validateSqlConnectionCredentials = async (
|
||||
) => {
|
||||
try {
|
||||
await executeWithPotentialGateway(config, gatewayService, async (client) => {
|
||||
await client.raw(`Select 1`);
|
||||
await client.raw(config.app === AppConnection.OracleDB ? `SELECT 1 FROM DUAL` : `Select 1`);
|
||||
});
|
||||
return config.credentials;
|
||||
} catch (error) {
|
||||
|
@@ -15,5 +15,16 @@ export type TIdentityAccessTokenJwtPayload = {
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
aws?: {
|
||||
accountId: string;
|
||||
arn: string;
|
||||
userId: string;
|
||||
|
||||
// Derived from ARN
|
||||
partition: string; // "aws", "aws-gov", "aws-cn"
|
||||
service: string; // "iam", "sts"
|
||||
resourceType: string; // "user" or "role"
|
||||
resourceName: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@@ -1,67 +1,91 @@
|
||||
interface PrincipalArnEntity {
|
||||
Partition: string;
|
||||
Service: "iam" | "sts";
|
||||
AccountNumber: string;
|
||||
Type: "user" | "role" | "instance-profile";
|
||||
Path: string;
|
||||
FriendlyName: string;
|
||||
SessionInfo: string; // Only populated for assumed-role
|
||||
}
|
||||
|
||||
export const extractPrincipalArnEntity = (arn: string): PrincipalArnEntity => {
|
||||
// split the ARN into parts using ":" as the delimiter
|
||||
const fullParts = arn.split(":");
|
||||
if (fullParts.length !== 6) {
|
||||
throw new Error(`Unrecognized ARN: "${arn}" contains ${fullParts.length} colon-separated parts, expected 6`);
|
||||
}
|
||||
const [prefix, partition, service, , accountNumber, resource] = fullParts;
|
||||
if (prefix !== "arn") {
|
||||
throw new Error(`Unrecognized ARN: "${arn}" does not begin with "arn:"`);
|
||||
}
|
||||
|
||||
// validate the service is either 'iam' or 'sts'
|
||||
if (service !== "iam" && service !== "sts") {
|
||||
throw new Error(`Unrecognized service: "${service}" in ARN "${arn}", expected "iam" or "sts"`);
|
||||
}
|
||||
|
||||
// parse the last part of the ARN which describes the resource
|
||||
const parts = resource.split("/");
|
||||
if (parts.length < 2) {
|
||||
throw new Error(
|
||||
`Unrecognized ARN: "${resource}" in ARN "${arn}" contains fewer than 2 slash-separated parts (expected type/name)`
|
||||
);
|
||||
}
|
||||
|
||||
const [rawType, ...rest] = parts;
|
||||
|
||||
let finalType: PrincipalArnEntity["Type"];
|
||||
let friendlyName: string = parts[parts.length - 1];
|
||||
let path: string = "";
|
||||
let sessionInfo: string = "";
|
||||
|
||||
// handle different types of resources
|
||||
switch (rawType) {
|
||||
case "assumed-role": {
|
||||
if (rest.length < 2) {
|
||||
throw new Error(
|
||||
`Unrecognized ARN: "${resource}" for assumed-role in ARN "${arn}" contains fewer than 3 slash-separated parts (type/roleName/sessionId)`
|
||||
);
|
||||
}
|
||||
// assumed roles use a special format where the friendly name is the role name
|
||||
const [roleName, sessionId] = rest;
|
||||
finalType = "role"; // treat assumed role case as role
|
||||
friendlyName = roleName;
|
||||
sessionInfo = sessionId;
|
||||
break;
|
||||
}
|
||||
case "user":
|
||||
case "role":
|
||||
case "instance-profile":
|
||||
finalType = rawType;
|
||||
path = rest.slice(0, -1).join("/");
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unrecognized principal type: "${rawType}" in ARN "${arn}". Expected "user", "role", "instance-profile", or "assumed-role".`
|
||||
);
|
||||
}
|
||||
|
||||
const entity: PrincipalArnEntity = {
|
||||
Partition: partition,
|
||||
Service: service,
|
||||
AccountNumber: accountNumber,
|
||||
Type: finalType,
|
||||
Path: path,
|
||||
FriendlyName: friendlyName,
|
||||
SessionInfo: sessionInfo
|
||||
};
|
||||
|
||||
return entity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the identity ARN from the GetCallerIdentity response to one of the following formats:
|
||||
* - arn:aws:iam::123456789012:user/MyUserName
|
||||
* - arn:aws:iam::123456789012:role/MyRoleName
|
||||
*/
|
||||
export const extractPrincipalArn = (arn: string) => {
|
||||
// split the ARN into parts using ":" as the delimiter
|
||||
const fullParts = arn.split(":");
|
||||
if (fullParts.length !== 6) {
|
||||
throw new Error(`Unrecognized ARN: contains ${fullParts.length} colon-separated parts, expected 6`);
|
||||
}
|
||||
const [prefix, partition, service, , accountNumber, resource] = fullParts;
|
||||
if (prefix !== "arn") {
|
||||
throw new Error('Unrecognized ARN: does not begin with "arn:"');
|
||||
}
|
||||
|
||||
// structure to hold the parsed data
|
||||
const entity = {
|
||||
Partition: partition,
|
||||
Service: service,
|
||||
AccountNumber: accountNumber,
|
||||
Type: "",
|
||||
Path: "",
|
||||
FriendlyName: "",
|
||||
SessionInfo: ""
|
||||
};
|
||||
|
||||
// validate the service is either 'iam' or 'sts'
|
||||
if (entity.Service !== "iam" && entity.Service !== "sts") {
|
||||
throw new Error(`Unrecognized service: ${entity.Service}, not one of iam or sts`);
|
||||
}
|
||||
|
||||
// parse the last part of the ARN which describes the resource
|
||||
const parts = resource.split("/");
|
||||
if (parts.length < 2) {
|
||||
throw new Error(`Unrecognized ARN: "${resource}" contains fewer than 2 slash-separated parts`);
|
||||
}
|
||||
|
||||
const [type, ...rest] = parts;
|
||||
entity.Type = type;
|
||||
entity.FriendlyName = parts[parts.length - 1];
|
||||
|
||||
// handle different types of resources
|
||||
switch (entity.Type) {
|
||||
case "assumed-role": {
|
||||
if (rest.length < 2) {
|
||||
throw new Error(`Unrecognized ARN: "${resource}" contains fewer than 3 slash-separated parts`);
|
||||
}
|
||||
// assumed roles use a special format where the friendly name is the role name
|
||||
const [roleName, sessionId] = rest;
|
||||
entity.Type = "role"; // treat assumed role case as role
|
||||
entity.FriendlyName = roleName;
|
||||
entity.SessionInfo = sessionId;
|
||||
break;
|
||||
}
|
||||
case "user":
|
||||
case "role":
|
||||
case "instance-profile":
|
||||
// standard cases: just join back the path if there's any
|
||||
entity.Path = rest.slice(0, -1).join("/");
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized principal type: "${entity.Type}"`);
|
||||
}
|
||||
const entity = extractPrincipalArnEntity(arn);
|
||||
|
||||
return `arn:aws:iam::${entity.AccountNumber}:${entity.Type}/${entity.FriendlyName}`;
|
||||
};
|
||||
|
@@ -22,7 +22,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
|
||||
import { TIdentityAwsAuthDALFactory } from "./identity-aws-auth-dal";
|
||||
import { extractPrincipalArn } from "./identity-aws-auth-fns";
|
||||
import { extractPrincipalArn, extractPrincipalArnEntity } from "./identity-aws-auth-fns";
|
||||
import {
|
||||
TAttachAwsAuthDTO,
|
||||
TAwsGetCallerIdentityHeaders,
|
||||
@@ -107,7 +107,7 @@ export const identityAwsAuthServiceFactory = ({
|
||||
const {
|
||||
data: {
|
||||
GetCallerIdentityResponse: {
|
||||
GetCallerIdentityResult: { Account, Arn }
|
||||
GetCallerIdentityResult: { Account, Arn, UserId }
|
||||
}
|
||||
}
|
||||
}: { data: TGetCallerIdentityResponse } = await axios({
|
||||
@@ -168,11 +168,25 @@ export const identityAwsAuthServiceFactory = ({
|
||||
});
|
||||
|
||||
const appCfg = getConfig();
|
||||
const splitArn = extractPrincipalArnEntity(Arn);
|
||||
const accessToken = crypto.jwt().sign(
|
||||
{
|
||||
identityId: identityAwsAuth.identityId,
|
||||
identityAccessTokenId: identityAccessToken.id,
|
||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN,
|
||||
identityAuth: {
|
||||
aws: {
|
||||
accountId: Account,
|
||||
arn: Arn,
|
||||
userId: UserId,
|
||||
|
||||
// Derived from ARN
|
||||
partition: splitArn.Partition,
|
||||
service: splitArn.Service,
|
||||
resourceType: splitArn.Type,
|
||||
resourceName: splitArn.FriendlyName
|
||||
}
|
||||
}
|
||||
} as TIdentityAccessTokenJwtPayload,
|
||||
appCfg.AUTH_SECRET,
|
||||
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
|
||||
|
@@ -11,7 +11,7 @@ import { TReminderServiceFactory } from "./reminder-types";
|
||||
type TDailyReminderQueueServiceFactoryDep = {
|
||||
reminderService: TReminderServiceFactory;
|
||||
queueService: TQueueServiceFactory;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "transaction" | "findSecretsWithReminderRecipients">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "transaction" | "findSecretsWithReminderRecipientsOld">;
|
||||
secretReminderRecipientsDAL: Pick<TSecretReminderRecipientsDALFactory, "delete">;
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@ export const dailyReminderQueueServiceFactory = ({
|
||||
|
||||
// Find existing secrets with pagination
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const secrets = await secretDAL.findSecretsWithReminderRecipients(batchIds, REMINDER_PRUNE_BATCH_SIZE);
|
||||
const secrets = await secretDAL.findSecretsWithReminderRecipientsOld(batchIds, REMINDER_PRUNE_BATCH_SIZE);
|
||||
const secretsWithReminder = secrets.filter((secret) => secret.reminderRepeatDays);
|
||||
|
||||
const foundSecretIds = new Set(secretsWithReminder.map((secret) => secret.id));
|
||||
|
@@ -308,12 +308,11 @@ export const reminderServiceFactory = ({
|
||||
);
|
||||
|
||||
const newReminders = await reminderDAL.insertMany(
|
||||
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate, projectId }) => ({
|
||||
processedReminders.map(({ secretId, message, repeatDays, nextReminderDate }) => ({
|
||||
secretId,
|
||||
message,
|
||||
repeatDays,
|
||||
nextReminderDate,
|
||||
projectId
|
||||
nextReminderDate
|
||||
})),
|
||||
tx
|
||||
);
|
||||
|
@@ -8,7 +8,26 @@ import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TRenderSecret, TRenderSyncWithCredentials } from "./render-sync-types";
|
||||
|
||||
const getRenderEnvironmentSecrets = async (secretSync: TRenderSyncWithCredentials) => {
|
||||
const MAX_RETRIES = 5;
|
||||
|
||||
const retrySleep = async () =>
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, 60000);
|
||||
});
|
||||
|
||||
const makeRequestWithRetry = async <T>(requestFn: () => Promise<T>, attempt = 0): Promise<T> => {
|
||||
try {
|
||||
return await requestFn();
|
||||
} catch (error) {
|
||||
if (isAxiosError(error) && error.response?.status === 429 && attempt < MAX_RETRIES) {
|
||||
await retrySleep();
|
||||
return await makeRequestWithRetry(requestFn, attempt + 1);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getRenderEnvironmentSecrets = async (secretSync: TRenderSyncWithCredentials): Promise<TRenderSecret[]> => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
@@ -22,20 +41,23 @@ const getRenderEnvironmentSecrets = async (secretSync: TRenderSyncWithCredential
|
||||
|
||||
do {
|
||||
const url = cursor ? `${baseUrl}?cursor=${cursor}` : baseUrl;
|
||||
const { data } = await request.get<
|
||||
{
|
||||
envVar: {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
cursor: string;
|
||||
}[]
|
||||
>(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
const { data } = await makeRequestWithRetry(() =>
|
||||
request.get<
|
||||
{
|
||||
envVar: {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
cursor: string;
|
||||
}[]
|
||||
>(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const secrets = data.map((item) => ({
|
||||
key: item.envVar.key,
|
||||
@@ -44,13 +66,20 @@ const getRenderEnvironmentSecrets = async (secretSync: TRenderSyncWithCredential
|
||||
|
||||
allSecrets.push(...secrets);
|
||||
|
||||
cursor = data[data.length - 1]?.cursor;
|
||||
if (data.length > 0 && data[data.length - 1]?.cursor) {
|
||||
cursor = data[data.length - 1].cursor;
|
||||
} else {
|
||||
cursor = undefined;
|
||||
}
|
||||
} while (cursor);
|
||||
|
||||
return allSecrets;
|
||||
};
|
||||
|
||||
const putEnvironmentSecret = async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap, key: string) => {
|
||||
const batchUpdateEnvironmentSecrets = async (
|
||||
secretSync: TRenderSyncWithCredentials,
|
||||
envVars: Array<{ key: string; value: string }>
|
||||
): Promise<void> => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
@@ -58,22 +87,17 @@ const putEnvironmentSecret = async (secretSync: TRenderSyncWithCredentials, secr
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
await request.put(
|
||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars/${key}`,
|
||||
{
|
||||
key,
|
||||
value: secretMap[key].value
|
||||
},
|
||||
{
|
||||
await makeRequestWithRetry(() =>
|
||||
request.put(`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars`, envVars, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const deleteEnvironmentSecret = async (secretSync: TRenderSyncWithCredentials, secret: Pick<TRenderSecret, "key">) => {
|
||||
const redeployService = async (secretSync: TRenderSyncWithCredentials) => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
@@ -81,70 +105,81 @@ const deleteEnvironmentSecret = async (secretSync: TRenderSyncWithCredentials, s
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
try {
|
||||
await request.delete(
|
||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/env-vars/${secret.key}`,
|
||||
await makeRequestWithRetry(() =>
|
||||
request.post(
|
||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${destinationConfig.serviceId}/deploys`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (isAxiosError(error) && error.response?.status === 404) {
|
||||
// If the secret does not exist, we can ignore this error
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const sleep = async () =>
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, 500);
|
||||
});
|
||||
|
||||
export const RenderSyncFns = {
|
||||
syncSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||
for await (const key of Object.keys(secretMap)) {
|
||||
// If value is empty skip it as render does not allow empty variables
|
||||
if (secretMap[key].value === "") {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
|
||||
const finalEnvVars: Array<{ key: string; value: string }> = [];
|
||||
|
||||
for (const renderSecret of renderSecrets) {
|
||||
const shouldKeep =
|
||||
secretMap[renderSecret.key] ||
|
||||
(secretSync.syncOptions.disableSecretDeletion &&
|
||||
!matchesSchema(renderSecret.key, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema));
|
||||
|
||||
if (shouldKeep && !secretMap[renderSecret.key]) {
|
||||
finalEnvVars.push({
|
||||
key: renderSecret.key,
|
||||
value: renderSecret.value
|
||||
});
|
||||
}
|
||||
await putEnvironmentSecret(secretSync, secretMap, key);
|
||||
await sleep();
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const renderSecret of renderSecrets) {
|
||||
if (!matchesSchema(renderSecret.key, secretSync.environment?.slug || "", secretSync.syncOptions.keySchema))
|
||||
for (const [key, secret] of Object.entries(secretMap)) {
|
||||
// Skip empty values as render does not allow empty variables
|
||||
if (secret.value === "") {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
|
||||
if (!secretMap[renderSecret.key]) {
|
||||
await deleteEnvironmentSecret(secretSync, renderSecret);
|
||||
await sleep();
|
||||
}
|
||||
|
||||
finalEnvVars.push({
|
||||
key,
|
||||
value: secret.value
|
||||
});
|
||||
}
|
||||
|
||||
await batchUpdateEnvironmentSecrets(secretSync, finalEnvVars);
|
||||
|
||||
if (secretSync.syncOptions.autoRedeployServices) {
|
||||
await redeployService(secretSync);
|
||||
}
|
||||
},
|
||||
|
||||
getSecrets: async (secretSync: TRenderSyncWithCredentials): Promise<TSecretMap> => {
|
||||
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||
return Object.fromEntries(renderSecrets.map((secret) => [secret.key, { value: secret.value ?? "" }]));
|
||||
},
|
||||
|
||||
removeSecrets: async (secretSync: TRenderSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const encryptedSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||
const renderSecrets = await getRenderEnvironmentSecrets(secretSync);
|
||||
const finalEnvVars: Array<{ key: string; value: string }> = [];
|
||||
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
if (encryptedSecret.key in secretMap) {
|
||||
await deleteEnvironmentSecret(secretSync, encryptedSecret);
|
||||
await sleep();
|
||||
for (const renderSecret of renderSecrets) {
|
||||
if (!(renderSecret.key in secretMap)) {
|
||||
finalEnvVars.push({
|
||||
key: renderSecret.key,
|
||||
value: renderSecret.value
|
||||
});
|
||||
}
|
||||
}
|
||||
await batchUpdateEnvironmentSecrets(secretSync, finalEnvVars);
|
||||
|
||||
if (secretSync.syncOptions.autoRedeployServices) {
|
||||
await redeployService(secretSync);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -20,23 +20,33 @@ const RenderSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
|
||||
})
|
||||
]);
|
||||
|
||||
const RenderSyncOptionsSchema = z.object({
|
||||
autoRedeployServices: z.boolean().optional().describe(SecretSyncs.ADDITIONAL_SYNC_OPTIONS.RENDER.autoRedeployServices)
|
||||
});
|
||||
|
||||
const RenderSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||
|
||||
export const RenderSyncSchema = BaseSecretSyncSchema(SecretSync.Render, RenderSyncOptionsConfig).extend({
|
||||
export const RenderSyncSchema = BaseSecretSyncSchema(
|
||||
SecretSync.Render,
|
||||
RenderSyncOptionsConfig,
|
||||
RenderSyncOptionsSchema
|
||||
).extend({
|
||||
destination: z.literal(SecretSync.Render),
|
||||
destinationConfig: RenderSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateRenderSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Render,
|
||||
RenderSyncOptionsConfig
|
||||
RenderSyncOptionsConfig,
|
||||
RenderSyncOptionsSchema
|
||||
).extend({
|
||||
destinationConfig: RenderSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateRenderSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Render,
|
||||
RenderSyncOptionsConfig
|
||||
RenderSyncOptionsConfig,
|
||||
RenderSyncOptionsSchema
|
||||
).extend({
|
||||
destinationConfig: RenderSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
@@ -875,6 +875,48 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findSecretsWithReminderRecipientsOld = async (ids: string[], limit: number, tx?: Knex) => {
|
||||
try {
|
||||
// Create a subquery to get limited secret IDs
|
||||
const limitedSecretIds = (tx || db)(TableName.SecretV2)
|
||||
.whereIn(`${TableName.SecretV2}.id`, ids)
|
||||
.limit(limit)
|
||||
.select("id");
|
||||
|
||||
// Join with all recipients for the limited secrets
|
||||
const docs = await (tx || db)(TableName.SecretV2)
|
||||
.whereIn(`${TableName.SecretV2}.id`, limitedSecretIds)
|
||||
.leftJoin(TableName.Reminder, `${TableName.SecretV2}.id`, `${TableName.Reminder}.secretId`)
|
||||
.leftJoin(
|
||||
TableName.SecretReminderRecipients,
|
||||
`${TableName.SecretV2}.id`,
|
||||
`${TableName.SecretReminderRecipients}.secretId`
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretV2))
|
||||
.select(db.ref("userId").withSchema(TableName.SecretReminderRecipients).as("reminderRecipientUserId"));
|
||||
|
||||
const data = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (el) => ({
|
||||
_id: el.id,
|
||||
...SecretsV2Schema.parse(el)
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reminderRecipientUserId",
|
||||
label: "recipients" as const,
|
||||
mapper: ({ reminderRecipientUserId }) => reminderRecipientUserId
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "findSecretsWithReminderRecipientsOld" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...secretOrm,
|
||||
update,
|
||||
@@ -893,6 +935,7 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
||||
findOne,
|
||||
find,
|
||||
invalidateSecretCacheByProjectId,
|
||||
findSecretsWithReminderRecipients
|
||||
findSecretsWithReminderRecipients,
|
||||
findSecretsWithReminderRecipientsOld
|
||||
};
|
||||
};
|
||||
|
@@ -206,13 +206,6 @@
|
||||
"documentation/platform/external-migrations/vault"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "External Migrations",
|
||||
"pages": [
|
||||
"documentation/platform/workflow-integrations/slack-integration",
|
||||
"documentation/platform/workflow-integrations/microsoft-teams-integration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Admin Consoles",
|
||||
"pages": [
|
||||
|
@@ -30,10 +30,10 @@ For methods like OIDC, these come as claims in the token and can be made availab
|
||||
|
||||
<Tabs>
|
||||
<Tab title="OIDC Login Attributes">
|
||||
1. Navigate to the Identity Authentication settings and select the OIDC Auth Method.
|
||||
2. In the **Advanced section**, locate the Claim Mapping configuration.
|
||||
3. Map the OIDC claims to permission attributes by specifying:
|
||||
- **Attribute Name:** The identifier to be used in your policies (e.g., department).
|
||||
1. Navigate to the Identity Authentication settings and select the OIDC Auth Method.
|
||||
2. In the **Advanced section**, locate the Claim Mapping configuration.
|
||||
3. Map the OIDC claims to permission attributes by specifying:
|
||||
- **Attribute Name:** The identifier to be used in your policies (e.g., department).
|
||||
- **Claim Path:** The dot notation path to the claim in the OIDC token (e.g., user.department).
|
||||
|
||||
For example, if your OIDC provider returns:
|
||||
@@ -64,7 +64,7 @@ For methods like OIDC, these come as claims in the token and can be made availab
|
||||
|
||||
</Tab>
|
||||
<Tab title="Kubernetes Login Attributes">
|
||||
For identities authenticated using Kubernetes, the service account's namespace and name are available in their policy and can be accessed as follows:
|
||||
For identities authenticated using Kubernetes, the service account's namespace and name are available in their policy and can be accessed as follows:
|
||||
|
||||
```
|
||||
{{ identity.auth.kubernetes.namespace }}
|
||||
@@ -72,9 +72,25 @@ For methods like OIDC, these come as claims in the token and can be made availab
|
||||
```
|
||||
|
||||
<img src="/images/platform/access-controls/abac-policy-k8s-format.png" />
|
||||
</Tab>
|
||||
<Tab title="AWS Attributes">
|
||||
For identities authenticated using AWS Auth, several attributes can be accessed. On top of the 3 base attributes, there's 4 derived from the ARN. The example below includes comments showing how each derived attribute looks like based on this ARN: `arn:aws:iam::123456789012:user/example-user`
|
||||
|
||||
```
|
||||
{{ identity.auth.aws.accountId }}
|
||||
{{ identity.auth.aws.arn }}
|
||||
{{ identity.auth.aws.userId }}
|
||||
|
||||
// Derived from ARN
|
||||
{{ identity.auth.aws.partition }} // aws
|
||||
{{ identity.auth.aws.service }} // iam
|
||||
{{ identity.auth.aws.resourceType }} // user
|
||||
{{ identity.auth.aws.resourceName }} // example-user
|
||||
```
|
||||
|
||||
<img src="/images/platform/access-controls/abac-policy-aws-format.png" />
|
||||
</Tab>
|
||||
<Tab title="Other Authentication Method Attributes">
|
||||
At the moment we only support OIDC claims. Payloads on other authentication methods are not yet accessible.
|
||||
At the moment we only support OIDC claims, Kubernetes attributes, and AWS attributes. Payloads on other authentication methods are not yet accessible.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
BIN
docs/images/platform/access-controls/abac-policy-aws-format.png
Normal file
BIN
docs/images/platform/access-controls/abac-policy-aws-format.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 819 KiB |
@@ -271,6 +271,8 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
className="w-full"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
{projectTemplates.length
|
||||
? projectTemplates.map((template) => (
|
||||
@@ -306,6 +308,9 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
|
||||
onChange(e);
|
||||
}}
|
||||
className="mb-12 w-full bg-mineshaft-600"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none -top-1"
|
||||
side="top"
|
||||
>
|
||||
<SelectItem value={INTERNAL_KMS_KEY_ID} key="kms-internal">
|
||||
Default Infisical KMS
|
||||
|
@@ -9,7 +9,7 @@ import {
|
||||
useRenderConnectionListServices
|
||||
} from "@app/hooks/api/appConnections/render";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/render-sync";
|
||||
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
|
@@ -0,0 +1,40 @@
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { FormControl, Switch, Tooltip } from "@app/components/v2";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
export const RenderSyncOptionsFields = () => {
|
||||
const { control } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="syncOptions.autoRedeployServices"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl className="mt-4" isError={Boolean(error?.message)} errorText={error?.message}>
|
||||
<Switch
|
||||
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
|
||||
id="auto-redeploy-services"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
isChecked={value}
|
||||
onCheckedChange={onChange}
|
||||
>
|
||||
Auto Redeploy Services On Sync
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
<p>If enabled, services will be automatically redeployed upon secret changes.</p>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
|
||||
</Tooltip>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
@@ -14,6 +14,7 @@ import { SecretSync, useSecretSyncOption } from "@app/hooks/api/secretSyncs";
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
import { AwsParameterStoreSyncOptionsFields } from "./AwsParameterStoreSyncOptionsFields";
|
||||
import { AwsSecretsManagerSyncOptionsFields } from "./AwsSecretsManagerSyncOptionsFields";
|
||||
import { RenderSyncOptionsFields } from "./RenderSyncOptionsFields";
|
||||
|
||||
type Props = {
|
||||
hideInitialSync?: boolean;
|
||||
@@ -38,6 +39,9 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
case SecretSync.AWSSecretsManager:
|
||||
AdditionalSyncOptionsFieldsComponent = <AwsSecretsManagerSyncOptionsFields />;
|
||||
break;
|
||||
case SecretSync.Render:
|
||||
AdditionalSyncOptionsFieldsComponent = <RenderSyncOptionsFields />;
|
||||
break;
|
||||
case SecretSync.GitHub:
|
||||
case SecretSync.GCPSecretManager:
|
||||
case SecretSync.AzureKeyVault:
|
||||
@@ -54,7 +58,6 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
case SecretSync.OnePass:
|
||||
case SecretSync.OCIVault:
|
||||
case SecretSync.Heroku:
|
||||
case SecretSync.Render:
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
|
@@ -2,8 +2,29 @@ import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Badge } from "@app/components/v2";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const RenderSyncOptionsReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
|
||||
|
||||
const [{ autoRedeployServices }] = watch(["syncOptions"]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{autoRedeployServices ? (
|
||||
<GenericFieldLabel label="Auto Redeploy Services">
|
||||
<Badge variant="success">Enabled</Badge>
|
||||
</GenericFieldLabel>
|
||||
) : (
|
||||
<GenericFieldLabel label="Auto Redeploy Services">
|
||||
<Badge variant="danger">Disabled</Badge>
|
||||
</GenericFieldLabel>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const RenderSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Render }>();
|
||||
const serviceName = watch("destinationConfig.serviceName");
|
||||
|
@@ -35,7 +35,7 @@ import { HumanitecSyncReviewFields } from "./HumanitecSyncReviewFields";
|
||||
import { OCIVaultSyncReviewFields } from "./OCIVaultSyncReviewFields";
|
||||
import { OnePassSyncReviewFields } from "./OnePassSyncReviewFields";
|
||||
import { RailwaySyncReviewFields } from "./RailwaySyncReviewFields";
|
||||
import { RenderSyncReviewFields } from "./RenderSyncReviewFields";
|
||||
import { RenderSyncOptionsReviewFields, RenderSyncReviewFields } from "./RenderSyncReviewFields";
|
||||
import { SupabaseSyncReviewFields } from "./SupabaseSyncReviewFields";
|
||||
import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields";
|
||||
import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields";
|
||||
@@ -121,6 +121,7 @@ export const SecretSyncReviewFields = () => {
|
||||
break;
|
||||
case SecretSync.Render:
|
||||
DestinationFieldsComponent = <RenderSyncReviewFields />;
|
||||
AdditionalSyncOptionsFieldsComponent = <RenderSyncOptionsReviewFields />;
|
||||
break;
|
||||
case SecretSync.Flyio:
|
||||
DestinationFieldsComponent = <FlyioSyncReviewFields />;
|
||||
|
@@ -2,9 +2,13 @@ import { z } from "zod";
|
||||
|
||||
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/render-sync";
|
||||
import { RenderSyncScope, RenderSyncType } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
export const RenderSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
||||
export const RenderSyncDestinationSchema = BaseSecretSyncSchema(
|
||||
z.object({
|
||||
autoRedeployServices: z.boolean().optional()
|
||||
})
|
||||
).merge(
|
||||
z.object({
|
||||
destination: z.literal(SecretSync.Render),
|
||||
destinationConfig: z.discriminatedUnion("scope", [
|
||||
|
@@ -20,6 +20,7 @@ type Props = {
|
||||
isMulti?: boolean;
|
||||
iconClassName?: string;
|
||||
dropdownContainerStyle?: React.CSSProperties;
|
||||
side?: SelectPrimitive.SelectContentProps["side"];
|
||||
};
|
||||
|
||||
export type SelectProps = Omit<SelectPrimitive.SelectProps, "disabled"> & Props;
|
||||
@@ -37,6 +38,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
containerClassName,
|
||||
iconClassName,
|
||||
dropdownContainerStyle,
|
||||
side,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
@@ -78,6 +80,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
</SelectPrimitive.Trigger>
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
side={side}
|
||||
className={twMerge(
|
||||
"relative top-1 z-[100] max-w-sm overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-900 font-inter text-bunker-100 shadow-md",
|
||||
position === "popper" && "max-h-72",
|
||||
|
@@ -4,9 +4,9 @@ import {
|
||||
SecretSyncImportBehavior,
|
||||
SecretSyncInitialSyncBehavior
|
||||
} from "@app/hooks/api/secretSyncs";
|
||||
import { RenderSyncScope } from "@app/hooks/api/secretSyncs/render-sync";
|
||||
import { GcpSyncScope } from "@app/hooks/api/secretSyncs/types/gcp-sync";
|
||||
import { HumanitecSyncScope } from "@app/hooks/api/secretSyncs/types/humanitec-sync";
|
||||
import { RenderSyncScope } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }> = {
|
||||
[SecretSync.AWSParameterStore]: { name: "AWS Parameter Store", image: "Amazon Web Services.png" },
|
||||
|
@@ -86,6 +86,7 @@ export const useCreateIdentity = () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -110,6 +111,7 @@ export const useUpdateIdentity = () => {
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -130,6 +132,7 @@ export const useDeleteIdentity = () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -25,7 +25,9 @@ import {
|
||||
|
||||
export const identitiesKeys = {
|
||||
getIdentityById: (identityId: string) => [{ identityId }, "identity"] as const,
|
||||
searchIdentities: (dto: TSearchIdentitiesDTO) => ["identity", "search", dto] as const,
|
||||
searchIdentitiesRoot: ["identity", "search"] as const,
|
||||
searchIdentities: (dto: TSearchIdentitiesDTO) =>
|
||||
[...identitiesKeys.searchIdentitiesRoot, dto] as const,
|
||||
getIdentityUniversalAuth: (identityId: string) =>
|
||||
[{ identityId }, "identity-universal-auth"] as const,
|
||||
getIdentityUniversalAuthClientSecrets: (identityId: string) =>
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { SecretSync, SecretSyncImportBehavior } from "@app/hooks/api/secretSyncs";
|
||||
import { DiscriminativePick } from "@app/types";
|
||||
|
||||
import { TRenderSync } from "../render-sync";
|
||||
import { TOnePassSync } from "./1password-sync";
|
||||
import { TAwsParameterStoreSync } from "./aws-parameter-store-sync";
|
||||
import { TAwsSecretsManagerSync } from "./aws-secrets-manager-sync";
|
||||
@@ -24,6 +23,7 @@ import { THerokuSync } from "./heroku-sync";
|
||||
import { THumanitecSync } from "./humanitec-sync";
|
||||
import { TOCIVaultSync } from "./oci-vault-sync";
|
||||
import { TRailwaySync } from "./railway-sync";
|
||||
import { TRenderSync } from "./render-sync";
|
||||
import { TSupabaseSync } from "./supabase";
|
||||
import { TTeamCitySync } from "./teamcity-sync";
|
||||
import { TTerraformCloudSync } from "./terraform-cloud-sync";
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
|
||||
import { RootSyncOptions, TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
|
||||
|
||||
export type TRenderSync = TRootSecretSync & {
|
||||
destination: SecretSync.Render;
|
||||
@@ -16,6 +16,10 @@ export type TRenderSync = TRootSecretSync & {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
syncOptions: RootSyncOptions & {
|
||||
autoRedeployServices?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export enum RenderSyncScope {
|
71
frontend/src/hooks/useResizableColWidth.tsx
Normal file
71
frontend/src/hooks/useResizableColWidth.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { MouseEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
type Params = {
|
||||
minWidth: number;
|
||||
maxWidth: number;
|
||||
initialWidth: number;
|
||||
};
|
||||
|
||||
export const useResizableColWidth = ({ minWidth, maxWidth, initialWidth }: Params) => {
|
||||
const [colWidth, setColWidth] = useState(initialWidth);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const startX = useRef(0);
|
||||
const startWidth = useRef(0);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsResizing(true);
|
||||
startX.current = e.clientX;
|
||||
startWidth.current = colWidth;
|
||||
},
|
||||
[colWidth]
|
||||
);
|
||||
|
||||
const handleMouseMove = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (!isResizing) return;
|
||||
|
||||
const deltaX = e.clientX - startX.current;
|
||||
const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidth.current + deltaX));
|
||||
|
||||
setColWidth(newWidth);
|
||||
},
|
||||
[isResizing]
|
||||
);
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setIsResizing(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isResizing) {
|
||||
document.addEventListener(
|
||||
"mousemove",
|
||||
// @ts-expect-error native discrepancy
|
||||
handleMouseMove
|
||||
);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
document.body.style.cursor = "ew-resize";
|
||||
document.body.style.userSelect = "none";
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener(
|
||||
"mousemove",
|
||||
// @ts-expect-error native discrepancy
|
||||
handleMouseMove
|
||||
);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
document.body.style.cursor = "";
|
||||
document.body.style.userSelect = "";
|
||||
};
|
||||
}, [isResizing, handleMouseMove, handleMouseUp]);
|
||||
|
||||
return {
|
||||
colWidth,
|
||||
handleMouseDown,
|
||||
isResizing
|
||||
};
|
||||
};
|
@@ -1,4 +1,4 @@
|
||||
import { faCog, faCube, faHome, faLock, faUsers } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faBook, faCog, faCube, faHome, faLock, faUsers } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { motion } from "framer-motion";
|
||||
@@ -84,6 +84,23 @@ export const KmsLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/kms/$projectId/audit-logs"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faBook} />
|
||||
</div>
|
||||
Audit Logs
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/kms/$projectId/settings"
|
||||
params={{
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
faBell,
|
||||
faBook,
|
||||
faCertificate,
|
||||
faCog,
|
||||
faFileLines,
|
||||
@@ -148,6 +149,23 @@ export const PkiManagerLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/cert-management/$projectId/audit-logs"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faBook} />
|
||||
</div>
|
||||
Audit Logs
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/cert-management/$projectId/settings"
|
||||
params={{
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
faArrowsSpin,
|
||||
faBook,
|
||||
faCheckToSlot,
|
||||
faCog,
|
||||
faHome,
|
||||
@@ -166,6 +167,23 @@ export const SecretManagerLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/secret-management/$projectId/audit-logs"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faBook} />
|
||||
</div>
|
||||
Audit Logs
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/secret-management/$projectId/settings"
|
||||
params={{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
faBook,
|
||||
faCog,
|
||||
faDatabase,
|
||||
faHome,
|
||||
@@ -90,6 +91,23 @@ export const SecretScanningLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/secret-scanning/$projectId/audit-logs"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faBook} />
|
||||
</div>
|
||||
Audit Logs
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/secret-scanning/$projectId/settings"
|
||||
params={{
|
||||
|
@@ -1,4 +1,11 @@
|
||||
import { faCog, faHome, faServer, faStamp, faUsers } from "@fortawesome/free-solid-svg-icons";
|
||||
import {
|
||||
faBook,
|
||||
faCog,
|
||||
faHome,
|
||||
faServer,
|
||||
faStamp,
|
||||
faUsers
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { motion } from "framer-motion";
|
||||
@@ -100,6 +107,23 @@ export const SshLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/ssh/$projectId/audit-logs"
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faBook} />
|
||||
</div>
|
||||
Audit Logs
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to="/projects/ssh/$projectId/settings"
|
||||
params={{
|
||||
|
@@ -29,6 +29,7 @@ import {
|
||||
} from "@app/hooks/api/auditLogs/constants";
|
||||
import { EventType } from "@app/hooks/api/auditLogs/enums";
|
||||
import { UserAgentType } from "@app/hooks/api/auth/types";
|
||||
import { Workspace } from "@app/hooks/api/workspace/types";
|
||||
|
||||
import { LogFilterItem } from "./LogFilterItem";
|
||||
import { auditLogFilterFormSchema, Presets, TAuditLogFilterFormData } from "./types";
|
||||
@@ -43,6 +44,7 @@ type Props = {
|
||||
presets?: Presets;
|
||||
setFilter: (data: TAuditLogFilterFormData) => void;
|
||||
filter: TAuditLogFilterFormData;
|
||||
project?: Workspace;
|
||||
};
|
||||
|
||||
const getActiveFilterCount = (filter: TAuditLogFilterFormData) => {
|
||||
@@ -69,7 +71,7 @@ const getActiveFilterCount = (filter: TAuditLogFilterFormData) => {
|
||||
return filterCount;
|
||||
};
|
||||
|
||||
export const LogsFilter = ({ presets, setFilter, filter }: Props) => {
|
||||
export const LogsFilter = ({ presets, setFilter, filter, project }: Props) => {
|
||||
const { data: workspaces = [] } = useGetUserWorkspaces();
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
@@ -90,7 +92,7 @@ export const LogsFilter = ({ presets, setFilter, filter }: Props) => {
|
||||
values: filter
|
||||
});
|
||||
const selectedEventTypes = watch("eventType") as EventType[] | undefined;
|
||||
const selectedProject = watch("project");
|
||||
const selectedProject = project ?? watch("project");
|
||||
|
||||
const showSecretsSection =
|
||||
selectedEventTypes?.some(
|
||||
@@ -270,48 +272,50 @@ export const LogsFilter = ({ presets, setFilter, filter }: Props) => {
|
||||
)}
|
||||
/>
|
||||
</LogFilterItem>
|
||||
<LogFilterItem
|
||||
label="Project"
|
||||
onClear={() => {
|
||||
resetField("project");
|
||||
resetField("environment");
|
||||
setValue("secretPath", "");
|
||||
setValue("secretKey", "");
|
||||
}}
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name="project"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
className="mb-0 w-full"
|
||||
>
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
isClearable
|
||||
onChange={(e) => {
|
||||
if (e === null) {
|
||||
setValue("secretPath", "");
|
||||
setValue("secretKey", "");
|
||||
}
|
||||
resetField("environment");
|
||||
onChange(e);
|
||||
}}
|
||||
placeholder="All projects"
|
||||
options={workspacesInOrg.map(({ name, id, type }) => ({
|
||||
name,
|
||||
id,
|
||||
type
|
||||
}))}
|
||||
getOptionValue={(option) => option.id}
|
||||
getOptionLabel={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</LogFilterItem>
|
||||
{!project && (
|
||||
<LogFilterItem
|
||||
label="Project"
|
||||
onClear={() => {
|
||||
resetField("project");
|
||||
resetField("environment");
|
||||
setValue("secretPath", "");
|
||||
setValue("secretKey", "");
|
||||
}}
|
||||
>
|
||||
<Controller
|
||||
control={control}
|
||||
name="project"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
className="mb-0 w-full"
|
||||
>
|
||||
<FilterableSelect
|
||||
value={value}
|
||||
isClearable
|
||||
onChange={(e) => {
|
||||
if (e === null) {
|
||||
setValue("secretPath", "");
|
||||
setValue("secretKey", "");
|
||||
}
|
||||
resetField("environment");
|
||||
onChange(e);
|
||||
}}
|
||||
placeholder="All projects"
|
||||
options={workspacesInOrg.map(({ name, id, type }) => ({
|
||||
name,
|
||||
id,
|
||||
type
|
||||
}))}
|
||||
getOptionValue={(option) => option.id}
|
||||
getOptionLabel={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</LogFilterItem>
|
||||
)}
|
||||
<AnimatePresence initial={false}>
|
||||
{showSecretsSection && (
|
||||
<motion.div
|
||||
|
@@ -7,6 +7,7 @@ import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
|
||||
import { Timezone } from "@app/helpers/datetime";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { Workspace } from "@app/hooks/api/workspace/types";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { LogsDateFilter } from "./LogsDateFilter";
|
||||
@@ -24,10 +25,11 @@ type Props = {
|
||||
refetchInterval?: number;
|
||||
showFilters?: boolean;
|
||||
pageView?: boolean;
|
||||
project?: Workspace;
|
||||
};
|
||||
|
||||
export const LogsSection = withPermission(
|
||||
({ presets, refetchInterval, showFilters = true, pageView = false }: Props) => {
|
||||
({ presets, refetchInterval, showFilters = true, pageView = false, project }: Props) => {
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||
@@ -83,7 +85,12 @@ export const LogsSection = withPermission(
|
||||
/>
|
||||
)}
|
||||
{showFilters && (
|
||||
<LogsFilter presets={presets} setFilter={setLogFilter} filter={logFilter} />
|
||||
<LogsFilter
|
||||
project={project}
|
||||
presets={presets}
|
||||
setFilter={setLogFilter}
|
||||
filter={logFilter}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -94,7 +101,7 @@ export const LogsSection = withPermission(
|
||||
secretPath: logFilter.secretPath || undefined,
|
||||
secretKey: logFilter.secretKey || undefined,
|
||||
eventMetadata: logFilter?.eventMetadata,
|
||||
projectId: logFilter?.project?.id,
|
||||
projectId: project?.id || logFilter?.project?.id,
|
||||
actorType: presets?.actorType,
|
||||
limit: 15,
|
||||
eventType: logFilter?.eventType,
|
||||
@@ -119,7 +126,7 @@ export const LogsSection = withPermission(
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex w-full justify-end">
|
||||
<div className="flex flex-wrap items-center gap-2 lg:justify-end">
|
||||
{showFilters && (
|
||||
<LogsDateFilter
|
||||
filter={dateFilter}
|
||||
|
@@ -52,7 +52,7 @@ export const ProjectsPage = () => {
|
||||
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
|
||||
|
||||
{!isLoading && !serverDetails?.redisConfigured && (
|
||||
<div className="mb-4 flex flex-col items-start justify-start text-3xl">
|
||||
<p className="mb-4 mr-4 font-semibold text-white">Announcements</p>
|
||||
|
27
frontend/src/pages/project/AuditLogsPage/AuditLogsPage.tsx
Normal file
27
frontend/src/pages/project/AuditLogsPage/AuditLogsPage.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { PageHeader } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { LogsSection } from "@app/pages/organization/AuditLogsPage/components";
|
||||
|
||||
export const AuditLogsPage = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<Helmet>
|
||||
<title>Project Audit Logs</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl">
|
||||
<PageHeader
|
||||
title="Audit logs"
|
||||
description="Audit logs for security and compliance teams to monitor information access."
|
||||
/>
|
||||
<LogsSection pageView project={currentWorkspace} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { AuditLogsPage } from "./AuditLogsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs"
|
||||
)({
|
||||
component: AuditLogsPage,
|
||||
beforeLoad: ({ context }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Audit Logs"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
19
frontend/src/pages/project/AuditLogsPage/route-kms.tsx
Normal file
19
frontend/src/pages/project/AuditLogsPage/route-kms.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { AuditLogsPage } from "./AuditLogsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs"
|
||||
)({
|
||||
component: AuditLogsPage,
|
||||
beforeLoad: ({ context }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Audit Logs"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { AuditLogsPage } from "./AuditLogsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs"
|
||||
)({
|
||||
component: AuditLogsPage,
|
||||
beforeLoad: ({ context }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Audit Logs"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { AuditLogsPage } from "./AuditLogsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs"
|
||||
)({
|
||||
component: AuditLogsPage,
|
||||
beforeLoad: ({ context }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Audit Logs"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
19
frontend/src/pages/project/AuditLogsPage/route-ssh.tsx
Normal file
19
frontend/src/pages/project/AuditLogsPage/route-ssh.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { AuditLogsPage } from "./AuditLogsPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs"
|
||||
)({
|
||||
component: AuditLogsPage,
|
||||
beforeLoad: ({ context }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Audit Logs"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
@@ -1,5 +1,5 @@
|
||||
import { useRenderConnectionListServices } from "@app/hooks/api/appConnections/render";
|
||||
import { TRenderSync } from "@app/hooks/api/secretSyncs/render-sync";
|
||||
import { TRenderSync } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
@@ -10,13 +10,12 @@ import {
|
||||
faArrowRight,
|
||||
faArrowRightToBracket,
|
||||
faArrowUp,
|
||||
faFileImport,
|
||||
faFilter,
|
||||
faFingerprint,
|
||||
faFolder,
|
||||
faFolderBlank,
|
||||
faFolderPlus,
|
||||
faKey,
|
||||
faList,
|
||||
faPlus,
|
||||
faRotate
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
@@ -96,7 +95,12 @@ import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { useUpdateFolderBatch } from "@app/hooks/api/secretFolders/queries";
|
||||
import { TUpdateFolderBatchDTO } from "@app/hooks/api/secretFolders/types";
|
||||
import { TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
import { SecretType, SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/types";
|
||||
import {
|
||||
SecretType,
|
||||
SecretV3RawSanitized,
|
||||
TSecretFolder,
|
||||
WorkspaceEnv
|
||||
} from "@app/hooks/api/types";
|
||||
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||
import {
|
||||
useDynamicSecretOverview,
|
||||
@@ -135,7 +139,6 @@ enum RowType {
|
||||
Folder = "folder",
|
||||
DynamicSecret = "dynamic",
|
||||
Secret = "secret",
|
||||
Import = "import",
|
||||
SecretRotation = "rotation"
|
||||
}
|
||||
|
||||
@@ -144,11 +147,10 @@ type Filter = {
|
||||
};
|
||||
|
||||
const DEFAULT_FILTER_STATE = {
|
||||
[RowType.Folder]: true,
|
||||
[RowType.DynamicSecret]: true,
|
||||
[RowType.Secret]: true,
|
||||
[RowType.Import]: true,
|
||||
[RowType.SecretRotation]: true
|
||||
[RowType.Folder]: false,
|
||||
[RowType.DynamicSecret]: false,
|
||||
[RowType.Secret]: false,
|
||||
[RowType.SecretRotation]: false
|
||||
};
|
||||
|
||||
const DEFAULT_COLLAPSED_HEADER_HEIGHT = 120;
|
||||
@@ -265,11 +267,8 @@ export const OverviewPage = () => {
|
||||
)
|
||||
);
|
||||
|
||||
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
||||
|
||||
useEffect(() => {
|
||||
setVisibleEnvs(userAvailableEnvs);
|
||||
}, [userAvailableEnvs]);
|
||||
const [filteredEnvs, setFilteredEnvs] = useState<WorkspaceEnv[]>([]);
|
||||
const visibleEnvs = filteredEnvs.length ? filteredEnvs : userAvailableEnvs;
|
||||
|
||||
const {
|
||||
secretImports,
|
||||
@@ -282,6 +281,7 @@ export const OverviewPage = () => {
|
||||
environments: (userAvailableEnvs || []).map(({ slug }) => slug)
|
||||
});
|
||||
|
||||
const isFilteredByResources = Object.values(filter).some(Boolean);
|
||||
const { isPending: isOverviewLoading, data: overview } = useGetProjectSecretsOverview(
|
||||
{
|
||||
projectId: workspaceId,
|
||||
@@ -289,11 +289,11 @@ export const OverviewPage = () => {
|
||||
secretPath,
|
||||
orderDirection,
|
||||
orderBy,
|
||||
includeFolders: filter.folder,
|
||||
includeDynamicSecrets: filter.dynamic,
|
||||
includeSecrets: filter.secret,
|
||||
includeImports: filter.import,
|
||||
includeSecretRotations: filter.rotation,
|
||||
includeFolders: isFilteredByResources ? filter.folder : true,
|
||||
includeDynamicSecrets: isFilteredByResources ? filter.dynamic : true,
|
||||
includeSecrets: isFilteredByResources ? filter.secret : true,
|
||||
includeImports: true,
|
||||
includeSecretRotations: isFilteredByResources ? filter.rotation : true,
|
||||
search: debouncedSearchFilter,
|
||||
limit,
|
||||
offset
|
||||
@@ -529,10 +529,10 @@ export const OverviewPage = () => {
|
||||
};
|
||||
|
||||
const handleEnvSelect = (envId: string) => {
|
||||
if (visibleEnvs.map((env) => env.id).includes(envId)) {
|
||||
setVisibleEnvs(visibleEnvs.filter((env) => env.id !== envId));
|
||||
if (filteredEnvs.map((env) => env.id).includes(envId)) {
|
||||
setFilteredEnvs(filteredEnvs.filter((env) => env.id !== envId));
|
||||
} else {
|
||||
setVisibleEnvs(visibleEnvs.concat(userAvailableEnvs.filter((env) => env.id === envId)));
|
||||
setFilteredEnvs(filteredEnvs.concat(userAvailableEnvs.filter((env) => env.id === envId)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -792,11 +792,11 @@ export const OverviewPage = () => {
|
||||
envNames: string[],
|
||||
envs: { environment: string; importedBy: ProjectSecretsImportedBy[] }[]
|
||||
): ProjectSecretsImportedBy[] => {
|
||||
const filteredEnvs = envs.filter((env) => envNames.includes(env.environment));
|
||||
const environments = envs.filter((env) => envNames.includes(env.environment));
|
||||
|
||||
if (filteredEnvs.length === 0) return [];
|
||||
if (environments.length === 0) return [];
|
||||
|
||||
const allImportedBy = filteredEnvs.flatMap((env) => env.importedBy);
|
||||
const allImportedBy = environments.flatMap((env) => env.importedBy);
|
||||
const groupedBySlug: Record<string, ProjectSecretsImportedBy[]> = {};
|
||||
|
||||
allImportedBy.forEach((item) => {
|
||||
@@ -902,9 +902,7 @@ export const OverviewPage = () => {
|
||||
|
||||
const isTableEmpty = totalCount === 0;
|
||||
|
||||
const isTableFiltered =
|
||||
Boolean(Object.values(filter).filter((enabled) => !enabled).length) ||
|
||||
userAvailableEnvs.length !== visibleEnvs.length;
|
||||
const isTableFiltered = isFilteredByResources || filteredEnvs.length > 0;
|
||||
|
||||
if (!isProjectV3)
|
||||
return (
|
||||
@@ -969,26 +967,42 @@ export const OverviewPage = () => {
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<FolderBreadCrumbs secretPath={secretPath} onResetSearch={handleResetSearch} />
|
||||
<div className="flex flex-row items-center justify-center space-x-2">
|
||||
{isTableFiltered && (
|
||||
<Button
|
||||
variant="plain"
|
||||
colorSchema="secondary"
|
||||
onClick={() => {
|
||||
setFilteredEnvs([]);
|
||||
setFilter(DEFAULT_FILTER_STATE);
|
||||
}}
|
||||
>
|
||||
Clear Filters
|
||||
</Button>
|
||||
)}
|
||||
{userAvailableEnvs.length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton
|
||||
ariaLabel="Environments"
|
||||
variant="plain"
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline_bg"
|
||||
className={twMerge(
|
||||
"flex h-10 w-11 items-center justify-center overflow-hidden border border-mineshaft-600 bg-mineshaft-800 p-0 transition-all hover:border-primary/60 hover:bg-primary/10",
|
||||
isTableFiltered && "border-primary/50 text-primary"
|
||||
"flex h-[2.5rem]",
|
||||
isTableFiltered && "border-primary/40 bg-primary/10"
|
||||
)}
|
||||
leftIcon={
|
||||
<FontAwesomeIcon
|
||||
icon={faFilter}
|
||||
className={isTableFiltered ? "text-primary/80" : undefined}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Tooltip content="Choose visible environments" className="mb-2">
|
||||
<FontAwesomeIcon icon={faList} />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
Filters
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="thin-scrollbar max-h-[70vh] overflow-y-auto"
|
||||
align="end"
|
||||
sideOffset={2}
|
||||
>
|
||||
{/* <DropdownMenuItem className="px-1.5" asChild>
|
||||
<Button
|
||||
@@ -1002,20 +1016,8 @@ export const OverviewPage = () => {
|
||||
Create an environment
|
||||
</Button>
|
||||
</DropdownMenuItem> */}
|
||||
<DropdownMenuLabel>Filter project resources</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleToggleRowType(RowType.Import);
|
||||
}}
|
||||
icon={filter[RowType.Import] && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FontAwesomeIcon icon={faFileImport} className="text-green-700" />
|
||||
<span>Imports</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuLabel>Filter by Resource</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -1070,11 +1072,11 @@ export const OverviewPage = () => {
|
||||
<span>Secrets</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuLabel>Choose visible environments</DropdownMenuLabel>
|
||||
<DropdownMenuLabel>Filter by Environment</DropdownMenuLabel>
|
||||
{userAvailableEnvs.map((availableEnv) => {
|
||||
const { id: envId, name } = availableEnv;
|
||||
|
||||
const isEnvSelected = visibleEnvs.map((env) => env.id).includes(envId);
|
||||
const isEnvSelected = filteredEnvs.map((env) => env.id).includes(envId);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
@@ -1082,7 +1084,6 @@ export const OverviewPage = () => {
|
||||
handleEnvSelect(envId);
|
||||
}}
|
||||
key={envId}
|
||||
disabled={visibleEnvs?.length === 1}
|
||||
icon={isEnvSelected && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
iconPos="right"
|
||||
>
|
||||
@@ -1405,13 +1406,6 @@ export const OverviewPage = () => {
|
||||
className="bg-mineshaft-700"
|
||||
/>
|
||||
)}
|
||||
{userAvailableEnvs.length > 0 && visibleEnvs.length === 0 && (
|
||||
<Tr>
|
||||
<Td colSpan={visibleEnvs.length + 1}>
|
||||
<EmptyState title="You have no visible environments" iconSize="3x" />
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
{userAvailableEnvs.length === 0 && (
|
||||
<Tr>
|
||||
<Td colSpan={visibleEnvs.length + 1}>
|
||||
@@ -1444,8 +1438,8 @@ export const OverviewPage = () => {
|
||||
<Td colSpan={visibleEnvs.length + 1}>
|
||||
<EmptyState
|
||||
title={
|
||||
debouncedSearchFilter
|
||||
? "No secret found for your search, add one now"
|
||||
isTableFiltered || debouncedSearchFilter
|
||||
? "No secrets found for your search, add one now"
|
||||
: "Let's add some secrets"
|
||||
}
|
||||
icon={faFolderBlank}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { subject } from "@casl/ability";
|
||||
@@ -53,6 +53,7 @@ import { PendingAction } from "@app/hooks/api/secretFolders/types";
|
||||
import { useCreateCommit } from "@app/hooks/api/secrets/mutations";
|
||||
import { SecretV3RawSanitized } from "@app/hooks/api/types";
|
||||
import { usePathAccessPolicies } from "@app/hooks/usePathAccessPolicies";
|
||||
import { useResizableColWidth } from "@app/hooks/useResizableColWidth";
|
||||
import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission";
|
||||
import { RequestAccessModal } from "@app/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/RequestAccessModal";
|
||||
import { SecretRotationListView } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretRotationListView";
|
||||
@@ -104,6 +105,8 @@ const Page = () => {
|
||||
const { permission } = useProjectPermission();
|
||||
const { mutateAsync: createCommit } = useCreateCommit();
|
||||
|
||||
const tableRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const { isBatchMode, pendingChanges } = useBatchMode();
|
||||
const { loadPendingChanges, setExistingKeys } = useBatchModeActions();
|
||||
@@ -212,11 +215,11 @@ const Page = () => {
|
||||
searchFilter: (routerQueryParams.search as string) || "",
|
||||
// these should always be on by default for the UI, they will be disabled for the query below based off permissions
|
||||
include: {
|
||||
[RowType.Folder]: true,
|
||||
[RowType.Import]: true,
|
||||
[RowType.DynamicSecret]: true,
|
||||
[RowType.Secret]: true,
|
||||
[RowType.SecretRotation]: true
|
||||
[RowType.Folder]: false,
|
||||
[RowType.Import]: false,
|
||||
[RowType.DynamicSecret]: false,
|
||||
[RowType.Secret]: false,
|
||||
[RowType.SecretRotation]: false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -242,6 +245,7 @@ const Page = () => {
|
||||
}
|
||||
}, [currentWorkspace, environment]);
|
||||
|
||||
const isResourceTypeFiltered = Object.values(filter.include).some(Boolean);
|
||||
const {
|
||||
data,
|
||||
isPending: isDetailsLoading,
|
||||
@@ -255,12 +259,14 @@ const Page = () => {
|
||||
orderBy,
|
||||
search: debouncedSearchFilter,
|
||||
orderDirection,
|
||||
includeImports: canReadSecretImports && filter.include.import,
|
||||
includeFolders: filter.include.folder,
|
||||
includeImports: canReadSecretImports && (isResourceTypeFiltered ? filter.include.import : true),
|
||||
includeFolders: isResourceTypeFiltered ? filter.include.folder : true,
|
||||
viewSecretValue: canReadSecretValue,
|
||||
includeDynamicSecrets: canReadDynamicSecret && filter.include.dynamic,
|
||||
includeSecrets: canReadSecret && filter.include.secret,
|
||||
includeSecretRotations: canReadSecretRotations && filter.include.rotation,
|
||||
includeDynamicSecrets:
|
||||
canReadDynamicSecret && (isResourceTypeFiltered ? filter.include.dynamic : true),
|
||||
includeSecrets: canReadSecret && (isResourceTypeFiltered ? filter.include.secret : true),
|
||||
includeSecretRotations:
|
||||
canReadSecretRotations && (isResourceTypeFiltered ? filter.include.rotation : true),
|
||||
tags: filter.tags
|
||||
});
|
||||
|
||||
@@ -480,6 +486,14 @@ const Page = () => {
|
||||
handlePopUpClose("snapshots");
|
||||
}, []);
|
||||
|
||||
const { handleMouseDown, isResizing, colWidth } = useResizableColWidth({
|
||||
initialWidth: 320,
|
||||
minWidth: 100,
|
||||
maxWidth: tableRef.current
|
||||
? tableRef.current.clientWidth - 148 // ensure value column can't collapse completely
|
||||
: 800
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// restore filters for path if set
|
||||
const restore = filterHistory.get(secretPath);
|
||||
@@ -769,8 +783,22 @@ const Page = () => {
|
||||
isPITEnabled={isPITEnabled}
|
||||
hasPathPolicies={hasPathPolicies}
|
||||
onRequestAccess={(params) => handlePopUpOpen("requestAccess", params)}
|
||||
onClearFilters={() =>
|
||||
setFilter((prev) => ({
|
||||
...prev,
|
||||
tags: {},
|
||||
include: {
|
||||
secret: false,
|
||||
import: false,
|
||||
dynamic: false,
|
||||
rotation: false,
|
||||
folder: false
|
||||
}
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<div
|
||||
ref={tableRef}
|
||||
className={twMerge(
|
||||
"thin-scrollbar mt-3 overflow-y-auto overflow-x-hidden rounded-md bg-mineshaft-800 text-left text-sm text-bunker-300",
|
||||
isNotEmpty && "rounded-b-none"
|
||||
@@ -804,20 +832,34 @@ const Page = () => {
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div
|
||||
className="flex w-80 flex-shrink-0 items-center border-r border-mineshaft-600 py-2 pl-4"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleSortToggle}
|
||||
onKeyDown={(evt) => {
|
||||
if (evt.key === "Enter") handleSortToggle();
|
||||
}}
|
||||
>
|
||||
Key
|
||||
<FontAwesomeIcon
|
||||
icon={orderDirection === OrderByDirection.ASC ? faArrowDown : faArrowUp}
|
||||
className="ml-2"
|
||||
<div className="relative">
|
||||
<div
|
||||
tabIndex={-1}
|
||||
role="button"
|
||||
className={`absolute -right-[0.05rem] z-40 h-full w-0.5 cursor-ew-resize hover:bg-blue-400/20 ${
|
||||
isResizing ? "bg-blue-400/75" : "bg-transparent"
|
||||
}`}
|
||||
onMouseDown={handleMouseDown}
|
||||
/>
|
||||
<div className="pointer-events-none absolute -right-[0.04rem] top-2 z-30">
|
||||
<div className="h-5 w-0.5 rounded-[1.5px] bg-gray-400 opacity-50" />
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-shrink-0 items-center border-r border-mineshaft-600 py-2 pl-4"
|
||||
style={{ width: colWidth }}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleSortToggle}
|
||||
onKeyDown={(evt) => {
|
||||
if (evt.key === "Enter") handleSortToggle();
|
||||
}}
|
||||
>
|
||||
Key
|
||||
<FontAwesomeIcon
|
||||
icon={orderDirection === OrderByDirection.ASC ? faArrowDown : faArrowUp}
|
||||
className="ml-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow px-4 py-2">Value</div>
|
||||
</div>
|
||||
@@ -911,6 +953,7 @@ const Page = () => {
|
||||
)}
|
||||
{canReadSecret && Boolean(mergedSecrets?.length) && (
|
||||
<SecretListView
|
||||
colWidth={colWidth}
|
||||
secrets={mergedSecrets}
|
||||
tags={tags}
|
||||
isVisible={isVisible}
|
||||
|
@@ -136,6 +136,7 @@ type Props = {
|
||||
isPITEnabled: boolean;
|
||||
onRequestAccess: (actions: ProjectPermissionActions[]) => void;
|
||||
hasPathPolicies: boolean;
|
||||
onClearFilters: () => void;
|
||||
};
|
||||
|
||||
export const ActionBar = ({
|
||||
@@ -159,7 +160,8 @@ export const ActionBar = ({
|
||||
isPITEnabled = false,
|
||||
usedBySecretSyncs,
|
||||
onRequestAccess,
|
||||
hasPathPolicies
|
||||
hasPathPolicies,
|
||||
onClearFilters
|
||||
}: Props) => {
|
||||
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
||||
"addFolder",
|
||||
@@ -661,6 +663,9 @@ export const ActionBar = ({
|
||||
}
|
||||
};
|
||||
|
||||
const isTableFiltered =
|
||||
Object.values(filter.tags).some(Boolean) || Object.values(filter.include).some(Boolean);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-4 flex items-center space-x-2">
|
||||
@@ -676,18 +681,22 @@ export const ActionBar = ({
|
||||
<div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline_bg"
|
||||
ariaLabel="Download"
|
||||
className={twMerge(
|
||||
"transition-all",
|
||||
(Object.keys(filter.tags).length ||
|
||||
Object.values(filter.include).filter((include) => !include).length) &&
|
||||
"border-primary/50 text-primary"
|
||||
"flex h-[2.5rem]",
|
||||
isTableFiltered && "border-primary/40 bg-primary/10"
|
||||
)}
|
||||
leftIcon={
|
||||
<FontAwesomeIcon
|
||||
icon={faFilter}
|
||||
className={isTableFiltered ? "text-primary/80" : undefined}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFilter} />
|
||||
</IconButton>
|
||||
Filters
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="p-0">
|
||||
<DropdownMenuGroup>Filter By</DropdownMenuGroup>
|
||||
@@ -762,51 +771,62 @@ export const ActionBar = ({
|
||||
<span>Secrets</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownSubMenu>
|
||||
<DropdownSubMenuTrigger
|
||||
iconPos="right"
|
||||
icon={<FontAwesomeIcon icon={faChevronRight} size="sm" />}
|
||||
>
|
||||
Tags
|
||||
</DropdownSubMenuTrigger>
|
||||
<DropdownSubMenuContent className="thin-scrollbar max-h-[20rem] overflow-y-auto rounded-l-none">
|
||||
<DropdownMenuLabel className="sticky top-0 bg-mineshaft-900">
|
||||
Apply Tags to Filter Secrets
|
||||
</DropdownMenuLabel>
|
||||
{tags.map(({ id, slug, color }) => (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
onToggleTagFilter(slug);
|
||||
}}
|
||||
key={id}
|
||||
icon={filter?.tags[slug] && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: color || "#bec2c8" }}
|
||||
/>
|
||||
{slug}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownSubMenuContent>
|
||||
</DropdownSubMenu>
|
||||
{Boolean(tags.length) && (
|
||||
<DropdownSubMenu>
|
||||
<DropdownSubMenuTrigger
|
||||
iconPos="right"
|
||||
icon={<FontAwesomeIcon icon={faChevronRight} size="sm" />}
|
||||
>
|
||||
Tags
|
||||
</DropdownSubMenuTrigger>
|
||||
<DropdownSubMenuContent className="thin-scrollbar max-h-[20rem] overflow-y-auto rounded-l-none">
|
||||
<DropdownMenuLabel className="sticky top-0 bg-mineshaft-900">
|
||||
Apply Tags to Filter Secrets
|
||||
</DropdownMenuLabel>
|
||||
{tags.map(({ id, slug, color }) => (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
onToggleTagFilter(slug);
|
||||
}}
|
||||
key={id}
|
||||
icon={filter?.tags[slug] && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: color || "#bec2c8" }}
|
||||
/>
|
||||
{slug}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownSubMenuContent>
|
||||
</DropdownSubMenu>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
{isTableFiltered && (
|
||||
<Button variant="plain" colorSchema="secondary" onClick={onClearFilters}>
|
||||
Clear Filters
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex-grow" />
|
||||
<div>
|
||||
{isProtectedBranch && (
|
||||
<Tooltip content={`Protected by policy ${protectedBranchPolicyName}`}>
|
||||
<IconButton variant="outline_bg" ariaLabel="protected">
|
||||
<IconButton
|
||||
variant="outline_bg"
|
||||
className="border-primary/40 bg-primary/10"
|
||||
ariaLabel="protected"
|
||||
>
|
||||
<FontAwesomeIcon icon={faLock} className="text-primary" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
<div>
|
||||
<IconButton variant="outline_bg" ariaLabel="Download" onClick={handleSecretDownload}>
|
||||
<FontAwesomeIcon icon={faDownload} />
|
||||
|
@@ -235,7 +235,7 @@ export const FolderListView = ({
|
||||
)}
|
||||
</div>
|
||||
{isPending ? (
|
||||
<div className="flex items-center space-x-4 border-l border-mineshaft-600 px-3 py-3">
|
||||
<div className="flex w-16 items-center justify-between border-l border-mineshaft-600 px-3 py-3">
|
||||
<IconButton
|
||||
ariaLabel="edit-folder"
|
||||
variant="plain"
|
||||
|
@@ -90,6 +90,7 @@ type Props = {
|
||||
}[];
|
||||
isPending?: boolean;
|
||||
pendingAction?: PendingAction;
|
||||
colWidth: number;
|
||||
};
|
||||
|
||||
export const SecretItem = memo(
|
||||
@@ -108,7 +109,8 @@ export const SecretItem = memo(
|
||||
handleSecretShare,
|
||||
importedBy,
|
||||
isPending,
|
||||
pendingAction
|
||||
pendingAction,
|
||||
colWidth
|
||||
}: Props) => {
|
||||
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
||||
"editSecret"
|
||||
@@ -383,7 +385,10 @@ export const SecretItem = memo(
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
|
||||
<div
|
||||
className="flex h-11 flex-shrink-0 items-center px-4 py-2"
|
||||
style={{ width: colWidth }}
|
||||
>
|
||||
<Controller
|
||||
name="key"
|
||||
control={control}
|
||||
|
@@ -51,6 +51,7 @@ type Props = {
|
||||
isImported: boolean;
|
||||
}[];
|
||||
}[];
|
||||
colWidth: number;
|
||||
};
|
||||
|
||||
export const SecretListView = ({
|
||||
@@ -62,7 +63,8 @@ export const SecretListView = ({
|
||||
isVisible,
|
||||
isProtectedBranch = false,
|
||||
usedBySecretSyncs,
|
||||
importedBy
|
||||
importedBy,
|
||||
colWidth
|
||||
}: Props) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
@@ -554,6 +556,7 @@ export const SecretListView = ({
|
||||
))}
|
||||
{secrets.map((secret) => (
|
||||
<SecretItem
|
||||
colWidth={colWidth}
|
||||
environment={environment}
|
||||
secretPath={secretPath}
|
||||
tags={wsTags}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { useRenderConnectionListServices } from "@app/hooks/api/appConnections/render";
|
||||
import { TRenderSync } from "@app/hooks/api/secretSyncs/render-sync";
|
||||
import { TRenderSync } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TRenderSync;
|
||||
|
@@ -0,0 +1,21 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { Badge } from "@app/components/v2";
|
||||
import { TRenderSync } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TRenderSync;
|
||||
};
|
||||
|
||||
export const RenderSyncOptionsSection = ({ secretSync }: Props) => {
|
||||
const {
|
||||
syncOptions: { autoRedeployServices }
|
||||
} = secretSync;
|
||||
|
||||
return (
|
||||
<GenericFieldLabel label="Auto Redeploy Services">
|
||||
<Badge variant={autoRedeployServices ? "success" : "danger"}>
|
||||
{autoRedeployServices ? "Enabled" : "Disabled"}
|
||||
</Badge>
|
||||
</GenericFieldLabel>
|
||||
);
|
||||
};
|
@@ -13,6 +13,7 @@ import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { AwsParameterStoreSyncOptionsSection } from "./AwsParameterStoreSyncOptionsSection";
|
||||
import { AwsSecretsManagerSyncOptionsSection } from "./AwsSecretsManagerSyncOptionsSection";
|
||||
import { RenderSyncOptionsSection } from "./RenderSyncOptionsSection";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
@@ -40,6 +41,9 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
<AwsSecretsManagerSyncOptionsSection secretSync={secretSync} />
|
||||
);
|
||||
break;
|
||||
case SecretSync.Render:
|
||||
AdditionalSyncOptionsComponent = <RenderSyncOptionsSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.GitHub:
|
||||
case SecretSync.GCPSecretManager:
|
||||
case SecretSync.AzureKeyVault:
|
||||
@@ -56,7 +60,6 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.OCIVault:
|
||||
case SecretSync.OnePass:
|
||||
case SecretSync.Heroku:
|
||||
case SecretSync.Render:
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
|
@@ -81,10 +81,15 @@ import { Route as secretManagerIntegrationsRouteBitbucketOauthRedirectImport } f
|
||||
import { Route as secretManagerIntegrationsRouteAzureKeyVaultOauthRedirectImport } from './pages/secret-manager/integrations/route-azure-key-vault-oauth-redirect'
|
||||
import { Route as secretManagerIntegrationsRouteAzureAppConfigurationsOauthRedirectImport } from './pages/secret-manager/integrations/route-azure-app-configurations-oauth-redirect'
|
||||
import { Route as organizationSettingsPageOauthCallbackPageRouteImport } from './pages/organization/SettingsPage/OauthCallbackPage/route'
|
||||
import { Route as projectAuditLogsPageRouteSshImport } from './pages/project/AuditLogsPage/route-ssh'
|
||||
import { Route as projectAccessControlPageRouteSshImport } from './pages/project/AccessControlPage/route-ssh'
|
||||
import { Route as projectAuditLogsPageRouteSecretScanningImport } from './pages/project/AuditLogsPage/route-secret-scanning'
|
||||
import { Route as projectAccessControlPageRouteSecretScanningImport } from './pages/project/AccessControlPage/route-secret-scanning'
|
||||
import { Route as projectAuditLogsPageRouteSecretManagerImport } from './pages/project/AuditLogsPage/route-secret-manager'
|
||||
import { Route as projectAccessControlPageRouteSecretManagerImport } from './pages/project/AccessControlPage/route-secret-manager'
|
||||
import { Route as projectAuditLogsPageRouteKmsImport } from './pages/project/AuditLogsPage/route-kms'
|
||||
import { Route as projectAccessControlPageRouteKmsImport } from './pages/project/AccessControlPage/route-kms'
|
||||
import { Route as projectAuditLogsPageRouteCertManagerImport } from './pages/project/AuditLogsPage/route-cert-manager'
|
||||
import { Route as projectAccessControlPageRouteCertManagerImport } from './pages/project/AccessControlPage/route-cert-manager'
|
||||
import { Route as sshSettingsPageRouteImport } from './pages/ssh/SettingsPage/route'
|
||||
import { Route as sshSshHostsPageRouteImport } from './pages/ssh/SshHostsPage/route'
|
||||
@@ -903,6 +908,13 @@ const organizationSettingsPageOauthCallbackPageRouteRoute =
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSettingsRoute,
|
||||
} as any)
|
||||
|
||||
const projectAuditLogsPageRouteSshRoute =
|
||||
projectAuditLogsPageRouteSshImport.update({
|
||||
id: '/audit-logs',
|
||||
path: '/audit-logs',
|
||||
getParentRoute: () => sshLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAccessControlPageRouteSshRoute =
|
||||
projectAccessControlPageRouteSshImport.update({
|
||||
id: '/access-management',
|
||||
@@ -919,6 +931,13 @@ const AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecret
|
||||
} as any,
|
||||
)
|
||||
|
||||
const projectAuditLogsPageRouteSecretScanningRoute =
|
||||
projectAuditLogsPageRouteSecretScanningImport.update({
|
||||
id: '/audit-logs',
|
||||
path: '/audit-logs',
|
||||
getParentRoute: () => secretScanningLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAccessControlPageRouteSecretScanningRoute =
|
||||
projectAccessControlPageRouteSecretScanningImport.update({
|
||||
id: '/access-management',
|
||||
@@ -935,6 +954,13 @@ const AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecr
|
||||
} as any,
|
||||
)
|
||||
|
||||
const projectAuditLogsPageRouteSecretManagerRoute =
|
||||
projectAuditLogsPageRouteSecretManagerImport.update({
|
||||
id: '/audit-logs',
|
||||
path: '/audit-logs',
|
||||
getParentRoute: () => secretManagerLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAccessControlPageRouteSecretManagerRoute =
|
||||
projectAccessControlPageRouteSecretManagerImport.update({
|
||||
id: '/access-management',
|
||||
@@ -942,6 +968,13 @@ const projectAccessControlPageRouteSecretManagerRoute =
|
||||
getParentRoute: () => secretManagerLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAuditLogsPageRouteKmsRoute =
|
||||
projectAuditLogsPageRouteKmsImport.update({
|
||||
id: '/audit-logs',
|
||||
path: '/audit-logs',
|
||||
getParentRoute: () => kmsLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAccessControlPageRouteKmsRoute =
|
||||
projectAccessControlPageRouteKmsImport.update({
|
||||
id: '/access-management',
|
||||
@@ -967,6 +1000,13 @@ const AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertMa
|
||||
} as any,
|
||||
)
|
||||
|
||||
const projectAuditLogsPageRouteCertManagerRoute =
|
||||
projectAuditLogsPageRouteCertManagerImport.update({
|
||||
id: '/audit-logs',
|
||||
path: '/audit-logs',
|
||||
getParentRoute: () => certManagerLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const projectAccessControlPageRouteCertManagerRoute =
|
||||
projectAccessControlPageRouteCertManagerImport.update({
|
||||
id: '/access-management',
|
||||
@@ -2722,6 +2762,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof projectAccessControlPageRouteCertManagerImport
|
||||
parentRoute: typeof certManagerLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs'
|
||||
path: '/audit-logs'
|
||||
fullPath: '/projects/cert-management/$projectId/audit-logs'
|
||||
preLoaderRoute: typeof projectAuditLogsPageRouteCertManagerImport
|
||||
parentRoute: typeof certManagerLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates'
|
||||
path: '/certificate-templates'
|
||||
@@ -2743,6 +2790,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof projectAccessControlPageRouteKmsImport
|
||||
parentRoute: typeof kmsLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs'
|
||||
path: '/audit-logs'
|
||||
fullPath: '/projects/kms/$projectId/audit-logs'
|
||||
preLoaderRoute: typeof projectAuditLogsPageRouteKmsImport
|
||||
parentRoute: typeof kmsLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management'
|
||||
path: '/access-management'
|
||||
@@ -2750,6 +2804,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof projectAccessControlPageRouteSecretManagerImport
|
||||
parentRoute: typeof secretManagerLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs'
|
||||
path: '/audit-logs'
|
||||
fullPath: '/projects/secret-management/$projectId/audit-logs'
|
||||
preLoaderRoute: typeof projectAuditLogsPageRouteSecretManagerImport
|
||||
parentRoute: typeof secretManagerLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations'
|
||||
path: '/integrations'
|
||||
@@ -2764,6 +2825,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof projectAccessControlPageRouteSecretScanningImport
|
||||
parentRoute: typeof secretScanningLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs'
|
||||
path: '/audit-logs'
|
||||
fullPath: '/projects/secret-scanning/$projectId/audit-logs'
|
||||
preLoaderRoute: typeof projectAuditLogsPageRouteSecretScanningImport
|
||||
parentRoute: typeof secretScanningLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources'
|
||||
path: '/data-sources'
|
||||
@@ -2778,6 +2846,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof projectAccessControlPageRouteSshImport
|
||||
parentRoute: typeof sshLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs'
|
||||
path: '/audit-logs'
|
||||
fullPath: '/projects/ssh/$projectId/audit-logs'
|
||||
preLoaderRoute: typeof projectAuditLogsPageRouteSshImport
|
||||
parentRoute: typeof sshLayoutImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates/': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates/'
|
||||
path: '/'
|
||||
@@ -3781,6 +3856,7 @@ interface certManagerLayoutRouteChildren {
|
||||
certManagerCertificatesPageRouteRoute: typeof certManagerCertificatesPageRouteRoute
|
||||
certManagerSettingsPageRouteRoute: typeof certManagerSettingsPageRouteRoute
|
||||
projectAccessControlPageRouteCertManagerRoute: typeof projectAccessControlPageRouteCertManagerRoute
|
||||
projectAuditLogsPageRouteCertManagerRoute: typeof projectAuditLogsPageRouteCertManagerRoute
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRouteWithChildren
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutSubscribersRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutSubscribersRouteWithChildren
|
||||
certManagerCertAuthDetailsByIDPageRouteRoute: typeof certManagerCertAuthDetailsByIDPageRouteRoute
|
||||
@@ -3799,6 +3875,8 @@ const certManagerLayoutRouteChildren: certManagerLayoutRouteChildren = {
|
||||
certManagerSettingsPageRouteRoute: certManagerSettingsPageRouteRoute,
|
||||
projectAccessControlPageRouteCertManagerRoute:
|
||||
projectAccessControlPageRouteCertManagerRoute,
|
||||
projectAuditLogsPageRouteCertManagerRoute:
|
||||
projectAuditLogsPageRouteCertManagerRoute,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRoute:
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRouteWithChildren,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutSubscribersRoute:
|
||||
@@ -3839,6 +3917,7 @@ interface kmsLayoutRouteChildren {
|
||||
kmsOverviewPageRouteRoute: typeof kmsOverviewPageRouteRoute
|
||||
kmsSettingsPageRouteRoute: typeof kmsSettingsPageRouteRoute
|
||||
projectAccessControlPageRouteKmsRoute: typeof projectAccessControlPageRouteKmsRoute
|
||||
projectAuditLogsPageRouteKmsRoute: typeof projectAuditLogsPageRouteKmsRoute
|
||||
projectGroupDetailsByIDPageRouteKmsRoute: typeof projectGroupDetailsByIDPageRouteKmsRoute
|
||||
projectIdentityDetailsByIDPageRouteKmsRoute: typeof projectIdentityDetailsByIDPageRouteKmsRoute
|
||||
projectMemberDetailsByIDPageRouteKmsRoute: typeof projectMemberDetailsByIDPageRouteKmsRoute
|
||||
@@ -3850,6 +3929,7 @@ const kmsLayoutRouteChildren: kmsLayoutRouteChildren = {
|
||||
kmsOverviewPageRouteRoute: kmsOverviewPageRouteRoute,
|
||||
kmsSettingsPageRouteRoute: kmsSettingsPageRouteRoute,
|
||||
projectAccessControlPageRouteKmsRoute: projectAccessControlPageRouteKmsRoute,
|
||||
projectAuditLogsPageRouteKmsRoute: projectAuditLogsPageRouteKmsRoute,
|
||||
projectGroupDetailsByIDPageRouteKmsRoute:
|
||||
projectGroupDetailsByIDPageRouteKmsRoute,
|
||||
projectIdentityDetailsByIDPageRouteKmsRoute:
|
||||
@@ -4166,6 +4246,7 @@ interface secretManagerLayoutRouteChildren {
|
||||
secretManagerSecretRotationPageRouteRoute: typeof secretManagerSecretRotationPageRouteRoute
|
||||
secretManagerSettingsPageRouteRoute: typeof secretManagerSettingsPageRouteRoute
|
||||
projectAccessControlPageRouteSecretManagerRoute: typeof projectAccessControlPageRouteSecretManagerRoute
|
||||
projectAuditLogsPageRouteSecretManagerRoute: typeof projectAuditLogsPageRouteSecretManagerRoute
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRouteWithChildren
|
||||
secretManagerSecretDashboardPageRouteRoute: typeof secretManagerSecretDashboardPageRouteRoute
|
||||
projectGroupDetailsByIDPageRouteSecretManagerRoute: typeof projectGroupDetailsByIDPageRouteSecretManagerRoute
|
||||
@@ -4186,6 +4267,8 @@ const secretManagerLayoutRouteChildren: secretManagerLayoutRouteChildren = {
|
||||
secretManagerSettingsPageRouteRoute: secretManagerSettingsPageRouteRoute,
|
||||
projectAccessControlPageRouteSecretManagerRoute:
|
||||
projectAccessControlPageRouteSecretManagerRoute,
|
||||
projectAuditLogsPageRouteSecretManagerRoute:
|
||||
projectAuditLogsPageRouteSecretManagerRoute,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRoute:
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRouteWithChildren,
|
||||
secretManagerSecretDashboardPageRouteRoute:
|
||||
@@ -4241,6 +4324,7 @@ interface secretScanningLayoutRouteChildren {
|
||||
secretScanningSecretScanningFindingsPageRouteRoute: typeof secretScanningSecretScanningFindingsPageRouteRoute
|
||||
secretScanningSettingsPageRouteRoute: typeof secretScanningSettingsPageRouteRoute
|
||||
projectAccessControlPageRouteSecretScanningRoute: typeof projectAccessControlPageRouteSecretScanningRoute
|
||||
projectAuditLogsPageRouteSecretScanningRoute: typeof projectAuditLogsPageRouteSecretScanningRoute
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRouteWithChildren
|
||||
projectGroupDetailsByIDPageRouteSecretScanningRoute: typeof projectGroupDetailsByIDPageRouteSecretScanningRoute
|
||||
projectIdentityDetailsByIDPageRouteSecretScanningRoute: typeof projectIdentityDetailsByIDPageRouteSecretScanningRoute
|
||||
@@ -4254,6 +4338,8 @@ const secretScanningLayoutRouteChildren: secretScanningLayoutRouteChildren = {
|
||||
secretScanningSettingsPageRouteRoute: secretScanningSettingsPageRouteRoute,
|
||||
projectAccessControlPageRouteSecretScanningRoute:
|
||||
projectAccessControlPageRouteSecretScanningRoute,
|
||||
projectAuditLogsPageRouteSecretScanningRoute:
|
||||
projectAuditLogsPageRouteSecretScanningRoute,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRoute:
|
||||
AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRouteWithChildren,
|
||||
projectGroupDetailsByIDPageRouteSecretScanningRoute:
|
||||
@@ -4289,6 +4375,7 @@ interface sshLayoutRouteChildren {
|
||||
sshSshHostsPageRouteRoute: typeof sshSshHostsPageRouteRoute
|
||||
sshSettingsPageRouteRoute: typeof sshSettingsPageRouteRoute
|
||||
projectAccessControlPageRouteSshRoute: typeof projectAccessControlPageRouteSshRoute
|
||||
projectAuditLogsPageRouteSshRoute: typeof projectAuditLogsPageRouteSshRoute
|
||||
sshSshCaByIDPageRouteRoute: typeof sshSshCaByIDPageRouteRoute
|
||||
sshSshHostGroupDetailsByIDPageRouteRoute: typeof sshSshHostGroupDetailsByIDPageRouteRoute
|
||||
projectGroupDetailsByIDPageRouteSshRoute: typeof projectGroupDetailsByIDPageRouteSshRoute
|
||||
@@ -4303,6 +4390,7 @@ const sshLayoutRouteChildren: sshLayoutRouteChildren = {
|
||||
sshSshHostsPageRouteRoute: sshSshHostsPageRouteRoute,
|
||||
sshSettingsPageRouteRoute: sshSettingsPageRouteRoute,
|
||||
projectAccessControlPageRouteSshRoute: projectAccessControlPageRouteSshRoute,
|
||||
projectAuditLogsPageRouteSshRoute: projectAuditLogsPageRouteSshRoute,
|
||||
sshSshCaByIDPageRouteRoute: sshSshCaByIDPageRouteRoute,
|
||||
sshSshHostGroupDetailsByIDPageRouteRoute:
|
||||
sshSshHostGroupDetailsByIDPageRouteRoute,
|
||||
@@ -4642,14 +4730,19 @@ export interface FileRoutesByFullPath {
|
||||
'/projects/ssh/$projectId/overview': typeof sshSshHostsPageRouteRoute
|
||||
'/projects/ssh/$projectId/settings': typeof sshSettingsPageRouteRoute
|
||||
'/projects/cert-management/$projectId/access-management': typeof projectAccessControlPageRouteCertManagerRoute
|
||||
'/projects/cert-management/$projectId/audit-logs': typeof projectAuditLogsPageRouteCertManagerRoute
|
||||
'/projects/cert-management/$projectId/certificate-templates': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRouteWithChildren
|
||||
'/projects/cert-management/$projectId/subscribers': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutSubscribersRouteWithChildren
|
||||
'/projects/kms/$projectId/access-management': typeof projectAccessControlPageRouteKmsRoute
|
||||
'/projects/kms/$projectId/audit-logs': typeof projectAuditLogsPageRouteKmsRoute
|
||||
'/projects/secret-management/$projectId/access-management': typeof projectAccessControlPageRouteSecretManagerRoute
|
||||
'/projects/secret-management/$projectId/audit-logs': typeof projectAuditLogsPageRouteSecretManagerRoute
|
||||
'/projects/secret-management/$projectId/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRouteWithChildren
|
||||
'/projects/secret-scanning/$projectId/access-management': typeof projectAccessControlPageRouteSecretScanningRoute
|
||||
'/projects/secret-scanning/$projectId/audit-logs': typeof projectAuditLogsPageRouteSecretScanningRoute
|
||||
'/projects/secret-scanning/$projectId/data-sources': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRouteWithChildren
|
||||
'/projects/ssh/$projectId/access-management': typeof projectAccessControlPageRouteSshRoute
|
||||
'/projects/ssh/$projectId/audit-logs': typeof projectAuditLogsPageRouteSshRoute
|
||||
'/projects/cert-management/$projectId/certificate-templates/': typeof certManagerPkiTemplateListPageRouteRoute
|
||||
'/projects/cert-management/$projectId/subscribers/': typeof certManagerPkiSubscribersPageRouteRoute
|
||||
'/projects/secret-management/$projectId/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
|
||||
@@ -4852,10 +4945,15 @@ export interface FileRoutesByTo {
|
||||
'/projects/ssh/$projectId/overview': typeof sshSshHostsPageRouteRoute
|
||||
'/projects/ssh/$projectId/settings': typeof sshSettingsPageRouteRoute
|
||||
'/projects/cert-management/$projectId/access-management': typeof projectAccessControlPageRouteCertManagerRoute
|
||||
'/projects/cert-management/$projectId/audit-logs': typeof projectAuditLogsPageRouteCertManagerRoute
|
||||
'/projects/kms/$projectId/access-management': typeof projectAccessControlPageRouteKmsRoute
|
||||
'/projects/kms/$projectId/audit-logs': typeof projectAuditLogsPageRouteKmsRoute
|
||||
'/projects/secret-management/$projectId/access-management': typeof projectAccessControlPageRouteSecretManagerRoute
|
||||
'/projects/secret-management/$projectId/audit-logs': typeof projectAuditLogsPageRouteSecretManagerRoute
|
||||
'/projects/secret-scanning/$projectId/access-management': typeof projectAccessControlPageRouteSecretScanningRoute
|
||||
'/projects/secret-scanning/$projectId/audit-logs': typeof projectAuditLogsPageRouteSecretScanningRoute
|
||||
'/projects/ssh/$projectId/access-management': typeof projectAccessControlPageRouteSshRoute
|
||||
'/projects/ssh/$projectId/audit-logs': typeof projectAuditLogsPageRouteSshRoute
|
||||
'/projects/cert-management/$projectId/certificate-templates': typeof certManagerPkiTemplateListPageRouteRoute
|
||||
'/projects/cert-management/$projectId/subscribers': typeof certManagerPkiSubscribersPageRouteRoute
|
||||
'/projects/secret-management/$projectId/integrations': typeof secretManagerIntegrationsListPageRouteRoute
|
||||
@@ -5075,14 +5173,19 @@ export interface FileRoutesById {
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/overview': typeof sshSshHostsPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/settings': typeof sshSettingsPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/access-management': typeof projectAccessControlPageRouteCertManagerRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs': typeof projectAuditLogsPageRouteCertManagerRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutCertificateTemplatesRouteWithChildren
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsCertManagementProjectIdCertManagerLayoutSubscribersRouteWithChildren
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/access-management': typeof projectAccessControlPageRouteKmsRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs': typeof projectAuditLogsPageRouteKmsRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management': typeof projectAccessControlPageRouteSecretManagerRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs': typeof projectAuditLogsPageRouteSecretManagerRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretManagementProjectIdSecretManagerLayoutIntegrationsRouteWithChildren
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/access-management': typeof projectAccessControlPageRouteSecretScanningRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs': typeof projectAuditLogsPageRouteSecretScanningRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources': typeof AuthenticateInjectOrgDetailsOrgLayoutProjectsSecretScanningProjectIdSecretScanningLayoutDataSourcesRouteWithChildren
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/access-management': typeof projectAccessControlPageRouteSshRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs': typeof projectAuditLogsPageRouteSshRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates/': typeof certManagerPkiTemplateListPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers/': typeof certManagerPkiSubscribersPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/': typeof secretManagerIntegrationsListPageRouteRoute
|
||||
@@ -5295,14 +5398,19 @@ export interface FileRouteTypes {
|
||||
| '/projects/ssh/$projectId/overview'
|
||||
| '/projects/ssh/$projectId/settings'
|
||||
| '/projects/cert-management/$projectId/access-management'
|
||||
| '/projects/cert-management/$projectId/audit-logs'
|
||||
| '/projects/cert-management/$projectId/certificate-templates'
|
||||
| '/projects/cert-management/$projectId/subscribers'
|
||||
| '/projects/kms/$projectId/access-management'
|
||||
| '/projects/kms/$projectId/audit-logs'
|
||||
| '/projects/secret-management/$projectId/access-management'
|
||||
| '/projects/secret-management/$projectId/audit-logs'
|
||||
| '/projects/secret-management/$projectId/integrations'
|
||||
| '/projects/secret-scanning/$projectId/access-management'
|
||||
| '/projects/secret-scanning/$projectId/audit-logs'
|
||||
| '/projects/secret-scanning/$projectId/data-sources'
|
||||
| '/projects/ssh/$projectId/access-management'
|
||||
| '/projects/ssh/$projectId/audit-logs'
|
||||
| '/projects/cert-management/$projectId/certificate-templates/'
|
||||
| '/projects/cert-management/$projectId/subscribers/'
|
||||
| '/projects/secret-management/$projectId/integrations/'
|
||||
@@ -5504,10 +5612,15 @@ export interface FileRouteTypes {
|
||||
| '/projects/ssh/$projectId/overview'
|
||||
| '/projects/ssh/$projectId/settings'
|
||||
| '/projects/cert-management/$projectId/access-management'
|
||||
| '/projects/cert-management/$projectId/audit-logs'
|
||||
| '/projects/kms/$projectId/access-management'
|
||||
| '/projects/kms/$projectId/audit-logs'
|
||||
| '/projects/secret-management/$projectId/access-management'
|
||||
| '/projects/secret-management/$projectId/audit-logs'
|
||||
| '/projects/secret-scanning/$projectId/access-management'
|
||||
| '/projects/secret-scanning/$projectId/audit-logs'
|
||||
| '/projects/ssh/$projectId/access-management'
|
||||
| '/projects/ssh/$projectId/audit-logs'
|
||||
| '/projects/cert-management/$projectId/certificate-templates'
|
||||
| '/projects/cert-management/$projectId/subscribers'
|
||||
| '/projects/secret-management/$projectId/integrations'
|
||||
@@ -5725,14 +5838,19 @@ export interface FileRouteTypes {
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/overview'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/settings'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/access-management'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/access-management'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/access-management'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/access-management'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates/'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers/'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations/'
|
||||
@@ -6331,6 +6449,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificates",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/settings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/access-management",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/subscribers",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/ca/$caName",
|
||||
@@ -6349,6 +6468,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/overview",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/settings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/access-management",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/groups/$groupId",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/identities/$identityId",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/members/$membershipId",
|
||||
@@ -6365,6 +6485,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/secret-rotation",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/settings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/secrets/$envSlug",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/groups/$groupId",
|
||||
@@ -6381,6 +6502,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/findings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/settings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/access-management",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/groups/$groupId",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/identities/$identityId",
|
||||
@@ -6397,6 +6519,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/overview",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/settings",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/access-management",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/ca/$caId",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId",
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/groups/$groupId",
|
||||
@@ -6485,6 +6608,10 @@ export const routeTree = rootRoute
|
||||
"filePath": "project/AccessControlPage/route-cert-manager.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/audit-logs": {
|
||||
"filePath": "project/AuditLogsPage/route-cert-manager.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates": {
|
||||
"filePath": "",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout",
|
||||
@@ -6504,10 +6631,18 @@ export const routeTree = rootRoute
|
||||
"filePath": "project/AccessControlPage/route-kms.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout/audit-logs": {
|
||||
"filePath": "project/AuditLogsPage/route-kms.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/kms/$projectId/_kms-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/access-management": {
|
||||
"filePath": "project/AccessControlPage/route-secret-manager.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/audit-logs": {
|
||||
"filePath": "project/AuditLogsPage/route-secret-manager.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout/integrations": {
|
||||
"filePath": "",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-management/$projectId/_secret-manager-layout",
|
||||
@@ -6596,6 +6731,10 @@ export const routeTree = rootRoute
|
||||
"filePath": "project/AccessControlPage/route-secret-scanning.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/audit-logs": {
|
||||
"filePath": "project/AuditLogsPage/route-secret-scanning.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout/data-sources": {
|
||||
"filePath": "",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/secret-scanning/$projectId/_secret-scanning-layout",
|
||||
@@ -6608,6 +6747,10 @@ export const routeTree = rootRoute
|
||||
"filePath": "project/AccessControlPage/route-ssh.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout/audit-logs": {
|
||||
"filePath": "project/AuditLogsPage/route-ssh.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/ssh/$projectId/_ssh-layout"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates/": {
|
||||
"filePath": "cert-manager/PkiTemplateListPage/route.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/projects/cert-management/$projectId/_cert-manager-layout/certificate-templates"
|
||||
|
@@ -63,6 +63,7 @@ const secretManagerRoutes = route("/projects/secret-management/$projectId", [
|
||||
)
|
||||
])
|
||||
]),
|
||||
route("/audit-logs", "project/AuditLogsPage/route-secret-manager.tsx"),
|
||||
route("/access-management", "project/AccessControlPage/route-secret-manager.tsx"),
|
||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-secret-manager.tsx"),
|
||||
route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-secret-manager.tsx"),
|
||||
@@ -312,6 +313,7 @@ const certManagerRoutes = route("/projects/cert-management/$projectId", [
|
||||
route("/ca/$caName", "cert-manager/CertAuthDetailsByIDPage/route.tsx"),
|
||||
route("/pki-collections/$collectionId", "cert-manager/PkiCollectionDetailsByIDPage/routes.tsx"),
|
||||
route("/settings", "cert-manager/SettingsPage/route.tsx"),
|
||||
route("/audit-logs", "project/AuditLogsPage/route-cert-manager.tsx"),
|
||||
route("/access-management", "project/AccessControlPage/route-cert-manager.tsx"),
|
||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-cert-manager.tsx"),
|
||||
route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-cert-manager.tsx"),
|
||||
@@ -325,6 +327,7 @@ const kmsRoutes = route("/projects/kms/$projectId", [
|
||||
route("/overview", "kms/OverviewPage/route.tsx"),
|
||||
route("/kmip", "kms/KmipPage/route.tsx"),
|
||||
route("/settings", "kms/SettingsPage/route.tsx"),
|
||||
route("/audit-logs", "project/AuditLogsPage/route-kms.tsx"),
|
||||
route("/access-management", "project/AccessControlPage/route-kms.tsx"),
|
||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-kms.tsx"),
|
||||
route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-kms.tsx"),
|
||||
@@ -341,6 +344,7 @@ const sshRoutes = route("/projects/ssh/$projectId", [
|
||||
route("/ca/$caId", "ssh/SshCaByIDPage/route.tsx"),
|
||||
route("/ssh-host-groups/$sshHostGroupId", "ssh/SshHostGroupDetailsByIDPage/route.tsx"),
|
||||
route("/settings", "ssh/SettingsPage/route.tsx"),
|
||||
route("/audit-logs", "project/AuditLogsPage/route-ssh.tsx"),
|
||||
route("/access-management", "project/AccessControlPage/route-ssh.tsx"),
|
||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-ssh.tsx"),
|
||||
route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-ssh.tsx"),
|
||||
@@ -357,6 +361,7 @@ const secretScanningRoutes = route("/projects/secret-scanning/$projectId", [
|
||||
]),
|
||||
route("/findings", "secret-scanning/SecretScanningFindingsPage/route.tsx"),
|
||||
route("/settings", "secret-scanning/SettingsPage/route.tsx"),
|
||||
route("/audit-logs", "project/AuditLogsPage/route-secret-scanning.tsx"),
|
||||
route("/access-management", "project/AccessControlPage/route-secret-scanning.tsx"),
|
||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-secret-scanning.tsx"),
|
||||
route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-secret-scanning.tsx"),
|
||||
|
Reference in New Issue
Block a user