feat(infisical-pg): completed support for tags in secret snapshot

This commit is contained in:
Akhil Mohan
2024-01-20 15:57:06 +05:30
parent b8157122e6
commit 42eb01e1e2
9 changed files with 88 additions and 15 deletions

View File

@@ -27,7 +27,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
status: null,
trial_end: null,
has_used_trial: true,
secretApproval: true,
secretApproval: false,
secretRotation: true
});

View File

@@ -1,12 +1,15 @@
import { ForbiddenError } from "@casl/ability";
import { TableName, TSecretTagJunctionInsert } from "@app/db/schemas";
import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TSecretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { TPermissionServiceFactory } from "../permission/permission-service";
@@ -31,6 +34,8 @@ type TSecretSnapshotServiceFactoryDep = {
"findLatestVersionByFolderId" | "insertMany"
>;
secretDAL: Pick<TSecretDALFactory, "delete" | "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
folderDAL: Pick<
TSecretFolderDALFactory,
"findById" | "findBySecretPath" | "delete" | "insertMany"
@@ -50,7 +55,9 @@ export const secretSnapshotServiceFactory = ({
folderDAL,
secretDAL,
permissionService,
licenseService
licenseService,
secretTagDAL,
secretVersionTagDAL
}: TSecretSnapshotServiceFactoryDep) => {
const projectSecretSnapshotCount = async ({
environment,
@@ -190,7 +197,11 @@ export const secretSnapshotServiceFactory = ({
folderVersion.map(({ name, id, latestFolderVersion }) => ({
envId: snapshot.envId,
id,
version: latestFolderVersion + 1,
// this means don't bump up the version if not root folder
// because below ones can be same version as nothing changed
version: deletedTopLevelFolders[folderId]
? latestFolderVersion + 1
: latestFolderVersion,
name,
parentId: folderId
}))
@@ -208,17 +219,33 @@ export const secretSnapshotServiceFactory = ({
secretId,
envId,
id,
tags,
...el
}) => ({
...el,
id: secretId,
version: latestSecretVersion + 1,
version: deletedTopLevelSecsGroupById[secretId]
? latestSecretVersion + 1
: latestSecretVersion,
folderId
})
)
),
tx
);
const secretTagsToBeInsert: TSecretTagJunctionInsert[] = [];
const secretVerTagToBeInsert: Record<string, string[]> = {};
rollbackSnaps.forEach(({ secretVersions }) => {
secretVersions.forEach((secVer) => {
secVer.tags.forEach((tag) => {
secretTagsToBeInsert.push({ secretsId: secVer.secretId, secret_tagsId: tag.id });
if (!secretVerTagToBeInsert?.[secVer.secretId])
secretVerTagToBeInsert[secVer.secretId] = [];
secretVerTagToBeInsert[secVer.secretId].push(tag.id);
});
});
});
await secretTagDAL.saveTagsToSecret(secretTagsToBeInsert, tx);
const folderVersions = await folderVersionDAL.insertMany(
folders.map(({ version, name, id, envId }) => ({
name,
@@ -232,6 +259,17 @@ export const secretSnapshotServiceFactory = ({
secrets.map(({ id, updatedAt, createdAt, ...el }) => ({ ...el, secretId: id })),
tx
);
await secretVersionTagDAL.insertMany(
secretVersions.flatMap(({ secretId, id }) =>
secretVerTagToBeInsert?.[secretId]?.length
? secretVerTagToBeInsert[secretId].map((tagId) => ({
[`${TableName.SecretTag}Id` as const]: tagId,
[`${TableName.SecretVersion}Id` as const]: id
}))
: []
),
tx
);
const newSnapshot = await snapshotDAL.create(
{
folderId: snapshot.folderId,

View File

@@ -223,6 +223,16 @@ export const snapshotDALFactory = (db: TDbClient) => {
`${TableName.SnapshotSecret}.secretVersionId`,
`${TableName.SecretVersion}.id`
)
.leftJoin(
TableName.SecretVersionTag,
`${TableName.SecretVersionTag}.${TableName.SecretVersion}Id`,
`${TableName.SecretVersion}.id`
)
.leftJoin(
TableName.SecretTag,
`${TableName.SecretVersionTag}.${TableName.SecretTag}Id`,
`${TableName.SecretTag}.id`
)
.leftJoin<{ latestSecretVersion: number }>(
(tx || db)(TableName.SecretVersion)
.groupBy("secretId")
@@ -249,8 +259,14 @@ export const snapshotDALFactory = (db: TDbClient) => {
db.ref("folderVerName").withSchema("parent"),
db.ref("folderVerId").withSchema("parent"),
db.ref("max").withSchema("secGroupByMaxVersion").as("latestSecretVersion"),
db.ref("max").withSchema("folderGroupByMaxVersion").as("latestFolderVersion")
db.ref("max").withSchema("folderGroupByMaxVersion").as("latestFolderVersion"),
db.ref("id").withSchema(TableName.SecretTag).as("tagId"),
db.ref("id").withSchema(TableName.SecretVersionTag).as("tagVersionId"),
db.ref("color").withSchema(TableName.SecretTag).as("tagColor"),
db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"),
db.ref("name").withSchema(TableName.SecretTag).as("tagName")
);
const formated = sqlNestRelationships({
data,
key: "snapshotId",
@@ -270,7 +286,20 @@ export const snapshotDALFactory = (db: TDbClient) => {
mapper: (el) => ({
...SecretVersionsSchema.parse(el),
latestSecretVersion: el.latestSecretVersion
})
}),
childrenMapper: [
{
key: "tagVersionId",
label: "tags" as const,
mapper: ({
tagId: id,
tagName: name,
tagSlug: slug,
tagColor: color,
tagVersionId: vId
}) => ({ id, name, slug, color, vId })
}
]
},
{
key: "folderVerId",

View File

@@ -122,7 +122,7 @@ export const sqlNestRelationships = <
lookupTable.add(pk);
}
sqlChildMapper(doc, recordsGroupedByPk, lookupTable, pk, "", childrenMapper);
sqlChildMapper(doc, recordsGroupedByPk, lookupTable, pk, pk, childrenMapper);
});
return recordsOrder.map((pkId) => recordsGroupedByPk[pkId]);
};

View File

@@ -300,6 +300,8 @@ export const registerRoutes = async (
const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDAL });
const snapshotService = secretSnapshotServiceFactory({
permissionService,
licenseService,
folderDAL,
secretDAL,
snapshotDAL,
@@ -307,8 +309,8 @@ export const registerRoutes = async (
snapshotSecretDAL,
secretVersionDAL,
folderVersionDAL,
permissionService,
licenseService
secretTagDAL,
secretVersionTagDAL
});
const webhookService = webhookServiceFactory({
permissionService,

View File

@@ -99,7 +99,8 @@ export const secretDALFactory = (db: TDbClient) => {
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(db.ref("name").withSchema(TableName.SecretTag).as("tagName"));
return sqlNestRelationships({
console.log(JSON.stringify(secs, null, 4));
const data = sqlNestRelationships({
data: secs,
key: "id",
parentMapper: (el) => SecretsSchema.parse(el),
@@ -116,6 +117,8 @@ export const secretDALFactory = (db: TDbClient) => {
}
]
});
console.log(JSON.stringify(data, null, 4));
return data;
} catch (error) {
throw new DatabaseError({ error, name: "get all secret" });
}

View File

@@ -164,17 +164,19 @@ export const secretServiceFactory = ({
tags !== undefined ? { tags, secretId: newSecrets[i].id } : []
);
if (secsUpdatedTag.length) {
await secretTagDAL.deleteTagsManySecret(
const delTags = await secretTagDAL.deleteTagsManySecret(
projectId,
secsUpdatedTag.map(({ secretId }) => secretId),
tx
);
console.log(delTags);
const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) =>
secretTags.map((tag) => ({
[`${TableName.SecretTag}Id` as const]: tag,
[`${TableName.Secret}Id` as const]: secretId
}))
);
console.log(newSecretTags);
if (newSecretTags.length) {
const secTags = await secretTagDAL.saveTagsToSecret(newSecretTags, tx);
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);

View File

@@ -325,7 +325,7 @@ export const SecretItem = memo(
return (
<DropdownMenuItem
onClick={() => handleTagSelect(tag)}
key={tagId}
key={`${secret.id}-${tagId}`}
icon={isTagSelected && <FontAwesomeIcon icon={faCheckCircle} />}
iconPos="right"
>
@@ -339,7 +339,7 @@ export const SecretItem = memo(
</DropdownMenuItem>
);
})}
<DropdownMenuItem className="px-1.5">
<DropdownMenuItem className="px-1.5" asChild>
<Button
size="xs"
className="w-full"

View File

@@ -97,7 +97,6 @@ export const SnapshotView = ({
{}
);
const diffView: Array<TDiffView<DecryptedSecret>> = [];
console.log({ rollingSecrets, secrets });
rollingSecrets.forEach((rollSecret) => {
const { id } = rollSecret;
const doesExist = Boolean(secretGroupById?.[id]);
@@ -119,7 +118,7 @@ export const SnapshotView = ({
});
return diffView;
}, [secrets, rollingSecrets]);
console.log(secretDiffView);
const handleClickRollback = async () => {
if (!snapshotData?.id) {
createNotification({