mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 16:27:40 +00:00
feat(secrets-ui): improve warning message and logic for secret-sync on secret imports
This commit is contained in:
@@ -1541,6 +1541,7 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const secretSyncService = secretSyncServiceFactory({
|
const secretSyncService = secretSyncServiceFactory({
|
||||||
secretSyncDAL,
|
secretSyncDAL,
|
||||||
|
secretImportDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
appConnectionService,
|
appConnectionService,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
.object({
|
.object({
|
||||||
secretId: z.string(),
|
secretId: z.string(),
|
||||||
referencedSecretKey: z.string(),
|
referencedSecretKey: z.string(),
|
||||||
referencedSecretId: z.string()
|
referencedSecretEnv: z.string()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -658,7 +658,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
.object({
|
.object({
|
||||||
secretId: z.string(),
|
secretId: z.string(),
|
||||||
referencedSecretKey: z.string(),
|
referencedSecretKey: z.string(),
|
||||||
referencedSecretId: z.string()
|
referencedSecretEnv: z.string()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -945,7 +945,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
||||||
name: sync.name,
|
name: sync.name,
|
||||||
destination: sync.destination,
|
destination: sync.destination,
|
||||||
environment
|
environment: sync.environment?.name || environment
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (secrets?.length || secretRotations?.length) {
|
if (secrets?.length || secretRotations?.length) {
|
||||||
|
|||||||
@@ -171,6 +171,19 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getFolderImports = async (secretPath: string, environmentId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const folderImports = await (tx || db.replicaNode())(TableName.SecretImport)
|
||||||
|
.where({ importPath: secretPath, importEnv: environmentId })
|
||||||
|
.join(TableName.SecretFolder, `${TableName.SecretImport}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
|
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.select(db.ref("id").withSchema(TableName.SecretFolder).as("folderId"));
|
||||||
|
return folderImports;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "get secret imports" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getFolderIsImportedBy = async (
|
const getFolderIsImportedBy = async (
|
||||||
secretPath: string,
|
secretPath: string,
|
||||||
environmentId: string,
|
environmentId: string,
|
||||||
@@ -204,7 +217,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||||
db.ref("id").withSchema(TableName.SecretFolder).as("folderId"),
|
db.ref("id").withSchema(TableName.SecretFolder).as("folderId"),
|
||||||
db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey"),
|
db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey"),
|
||||||
db.ref("id").withSchema(TableName.SecretV2).as("referencedSecretId")
|
db.ref("environment").withSchema(TableName.SecretReferenceV2).as("referencedSecretEnv")
|
||||||
);
|
);
|
||||||
|
|
||||||
const folderResults = folderImports.map(({ envName, envSlug, folderName, folderId }) => ({
|
const folderResults = folderImports.map(({ envName, envSlug, folderName, folderId }) => ({
|
||||||
@@ -215,14 +228,14 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const secretResults = secretReferences.map(
|
const secretResults = secretReferences.map(
|
||||||
({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey, referencedSecretId }) => ({
|
({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey, referencedSecretEnv }) => ({
|
||||||
envName,
|
envName,
|
||||||
envSlug,
|
envSlug,
|
||||||
secretId,
|
secretId,
|
||||||
folderName,
|
folderName,
|
||||||
folderId,
|
folderId,
|
||||||
referencedSecretKey,
|
referencedSecretKey,
|
||||||
referencedSecretId
|
referencedSecretEnv
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -237,7 +250,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
secrets: {
|
secrets: {
|
||||||
secretId: string;
|
secretId: string;
|
||||||
referencedSecretKey: string;
|
referencedSecretKey: string;
|
||||||
referencedSecretId: string;
|
referencedSecretEnv: string;
|
||||||
}[];
|
}[];
|
||||||
folderId: string;
|
folderId: string;
|
||||||
folderImported: boolean;
|
folderImported: boolean;
|
||||||
@@ -270,7 +283,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
{
|
{
|
||||||
secretId: item.secretId,
|
secretId: item.secretId,
|
||||||
referencedSecretKey: item.referencedSecretKey,
|
referencedSecretKey: item.referencedSecretKey,
|
||||||
referencedSecretId: item.referencedSecretId
|
referencedSecretEnv: item.referencedSecretEnv
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
@@ -316,6 +329,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
|||||||
findLastImportPosition,
|
findLastImportPosition,
|
||||||
updateAllPosition,
|
updateAllPosition,
|
||||||
getProjectImportCount,
|
getProjectImportCount,
|
||||||
getFolderIsImportedBy
|
getFolderIsImportedBy,
|
||||||
|
getFolderImports
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -878,7 +878,7 @@ export const secretImportServiceFactory = ({
|
|||||||
.map((otherSecret) => ({
|
.map((otherSecret) => ({
|
||||||
secretId: secret.secretKey,
|
secretId: secret.secretKey,
|
||||||
referencedSecretKey: otherSecret.secretKey,
|
referencedSecretKey: otherSecret.secretKey,
|
||||||
referencedSecretId: otherSecret.id
|
referencedSecretEnv: environment
|
||||||
}));
|
}));
|
||||||
}) || [];
|
}) || [];
|
||||||
if (locallyReferenced.length > 0) {
|
if (locallyReferenced.length > 0) {
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ export type FolderResult = {
|
|||||||
export type SecretResult = {
|
export type SecretResult = {
|
||||||
secretId: string;
|
secretId: string;
|
||||||
referencedSecretKey: string;
|
referencedSecretKey: string;
|
||||||
referencedSecretId: string;
|
referencedSecretEnv: string;
|
||||||
} & FolderResult;
|
} & FolderResult;
|
||||||
|
|
||||||
export type FolderInfo = {
|
export type FolderInfo = {
|
||||||
folderName: string;
|
folderName: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
folderId: string;
|
folderId: string;
|
||||||
folderImported: boolean;
|
folderImported: boolean;
|
||||||
envSlug?: string;
|
envSlug?: string;
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ import {
|
|||||||
TUpdateSecretSyncDTO
|
TUpdateSecretSyncDTO
|
||||||
} from "@app/services/secret-sync/secret-sync-types";
|
} from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
import { TSecretSyncDALFactory } from "./secret-sync-dal";
|
import { TSecretSyncDALFactory } from "./secret-sync-dal";
|
||||||
import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps";
|
import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps";
|
||||||
import { TSecretSyncQueueFactory } from "./secret-sync-queue";
|
import { TSecretSyncQueueFactory } from "./secret-sync-queue";
|
||||||
|
|
||||||
type TSecretSyncServiceFactoryDep = {
|
type TSecretSyncServiceFactoryDep = {
|
||||||
secretSyncDAL: TSecretSyncDALFactory;
|
secretSyncDAL: TSecretSyncDALFactory;
|
||||||
|
secretImportDAL: TSecretImportDALFactory;
|
||||||
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
|
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||||
@@ -54,6 +56,7 @@ export type TSecretSyncServiceFactory = ReturnType<typeof secretSyncServiceFacto
|
|||||||
export const secretSyncServiceFactory = ({
|
export const secretSyncServiceFactory = ({
|
||||||
secretSyncDAL,
|
secretSyncDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
secretImportDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
appConnectionService,
|
appConnectionService,
|
||||||
projectBotService,
|
projectBotService,
|
||||||
@@ -87,7 +90,7 @@ export const secretSyncServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const listSecretSyncsBySecretPath = async (
|
const listSecretSyncsBySecretPath = async (
|
||||||
{ projectId, secretPath, environment, destination }: TListSecretSyncsByFolderId,
|
{ projectId, secretPath, environment }: TListSecretSyncsByFolderId,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
@@ -106,9 +109,12 @@ export const secretSyncServiceFactory = ({
|
|||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder) return [];
|
if (!folder) return [];
|
||||||
|
|
||||||
|
const folderImports = await secretImportDAL.getFolderImports(secretPath, folder.envId);
|
||||||
|
|
||||||
const secretSyncs = await secretSyncDAL.find({
|
const secretSyncs = await secretSyncDAL.find({
|
||||||
...(destination && { destination }),
|
$in: {
|
||||||
folderId: folder.id
|
folderId: folderImports.map((folderImport) => folderImport.folderId).concat(folder.id)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return secretSyncs as TSecretSync[];
|
return secretSyncs as TSecretSync[];
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export const DeleteActionModal = ({
|
|||||||
buttonText = "Delete",
|
buttonText = "Delete",
|
||||||
formContent,
|
formContent,
|
||||||
deletionMessage,
|
deletionMessage,
|
||||||
buttonColorSchema,
|
buttonColorSchema = "danger",
|
||||||
children
|
children
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const [inputData, setInputData] = useState("");
|
const [inputData, setInputData] = useState("");
|
||||||
@@ -69,7 +69,7 @@ export const DeleteActionModal = ({
|
|||||||
<div className="mx-2 flex items-center">
|
<div className="mx-2 flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
colorSchema={buttonColorSchema || "danger"}
|
colorSchema={buttonColorSchema}
|
||||||
isDisabled={!(deleteKey === inputData) || isLoading}
|
isDisabled={!(deleteKey === inputData) || isLoading}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export type ProjectSecretsImportedBy = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -769,7 +769,7 @@ export const OverviewPage = () => {
|
|||||||
const uniqueSecrets: {
|
const uniqueSecrets: {
|
||||||
secretId: string;
|
secretId: string;
|
||||||
referencedSecretKey: string;
|
referencedSecretKey: string;
|
||||||
referencedSecretId: string;
|
referencedSecretEnv: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
const secretIds = new Set<string>();
|
const secretIds = new Set<string>();
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -59,7 +59,7 @@ type Props = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
@@ -133,7 +133,10 @@ export const SecretEditRow = ({
|
|||||||
importedBy &&
|
importedBy &&
|
||||||
importedBy.some(({ folders }) =>
|
importedBy.some(({ folders }) =>
|
||||||
folders?.some(({ secrets }) =>
|
folders?.some(({ secrets }) =>
|
||||||
secrets?.some(({ referencedSecretId }) => referencedSecretId === secretId)
|
secrets?.some(
|
||||||
|
({ referencedSecretKey, referencedSecretEnv }) =>
|
||||||
|
referencedSecretKey === secretName && referencedSecretEnv === environment
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ type Props = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ type Props = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
|||||||
+16
-4
@@ -26,7 +26,7 @@ interface CollapsibleSecretImportsProps {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
@@ -179,15 +179,27 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alertColors = onlyReferences
|
||||||
|
? {
|
||||||
|
border: "border-yellow-700/30",
|
||||||
|
bg: "bg-yellow-900/20",
|
||||||
|
text: "text-yellow-500"
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
border: "border-red-700/30",
|
||||||
|
bg: "bg-red-900/20",
|
||||||
|
text: "text-red-500"
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<div className="mb-4 rounded-md border border-red-700/30 bg-red-900/20">
|
<div className={`mb-4 rounded-md border ${alertColors.border} ${alertColors.bg}`}>
|
||||||
<div className="flex items-start gap-3 p-4">
|
<div className="flex items-start gap-3 p-4">
|
||||||
<div className="mt-0.5 flex-shrink-0 text-red-500">
|
<div className={`mt-0.5 flex-shrink-0 ${alertColors.text}`}>
|
||||||
<FontAwesomeIcon icon={faWarning} className="h-5 w-5" aria-hidden="true" />
|
<FontAwesomeIcon icon={faWarning} className="h-5 w-5" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<p className="text-sm font-semibold text-red-500">
|
<p className={`text-sm font-semibold ${alertColors.text}`}>
|
||||||
The following resources will be affected by this change
|
The following resources will be affected by this change
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+7
-8
@@ -81,7 +81,7 @@ type Props = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
@@ -227,13 +227,12 @@ export const SecretItem = memo(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (data: TFormSchema) => {
|
const handleFormSubmit = async (data: TFormSchema) => {
|
||||||
const hasDirectReferences =
|
const hasDirectReferences = importedBy?.some(({ folders }) =>
|
||||||
importedBy &&
|
folders?.some(({ secrets }) =>
|
||||||
importedBy.some(({ folders }) =>
|
secrets?.some(({ referencedSecretKey }) => referencedSecretKey === secret.key)
|
||||||
folders?.some(({ secrets }) =>
|
)
|
||||||
secrets?.some(({ referencedSecretId }) => referencedSecretId === secret.id)
|
);
|
||||||
)
|
|
||||||
);
|
|
||||||
if (hasDirectReferences) {
|
if (hasDirectReferences) {
|
||||||
handlePopUpOpen("editSecret", data);
|
handlePopUpOpen("editSecret", data);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ type Props = {
|
|||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
name: string;
|
name: string;
|
||||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretId: string }[];
|
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
|||||||
Reference in New Issue
Block a user