feat: addressed backend review changes needed by scott

This commit is contained in:
=
2024-10-18 00:33:36 +05:30
parent 43cfd63660
commit 936a48f458
16 changed files with 104 additions and 93 deletions
@@ -39,6 +39,8 @@ export async function up(knex: Knex): Promise<void> {
} }
} }
// secret permission is split into multiple ones like secrets, folders, imports and dynamic-secrets
// so we just find all the privileges with respective mapping and map it as needed
const identityPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select("*"); const identityPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select("*");
const updatedIdentityPrivilegesDocs = identityPrivileges const updatedIdentityPrivilegesDocs = identityPrivileges
.filter((i) => { .filter((i) => {
@@ -63,7 +65,7 @@ export async function up(knex: Knex): Promise<void> {
} }
const userPrivileges = await knex(TableName.ProjectUserAdditionalPrivilege).select("*"); const userPrivileges = await knex(TableName.ProjectUserAdditionalPrivilege).select("*");
const updatedUserPriviegeDocs = userPrivileges const updatedUserPrivilegeDocs = userPrivileges
.filter((i) => { .filter((i) => {
const permissionString = JSON.stringify(i.permissions || []); const permissionString = JSON.stringify(i.permissions || []);
return ( return (
@@ -79,8 +81,8 @@ export async function up(knex: Knex): Promise<void> {
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(unpackRules(el.permissions)))) permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(unpackRules(el.permissions))))
})); }));
if (docs.length) { if (docs.length) {
for (let i = 0; i < updatedUserPriviegeDocs.length; i += CHUNK_SIZE) { for (let i = 0; i < updatedUserPrivilegeDocs.length; i += CHUNK_SIZE) {
const chunk = updatedUserPriviegeDocs.slice(i, i + CHUNK_SIZE); const chunk = updatedUserPrivilegeDocs.slice(i, i + CHUNK_SIZE);
await knex(TableName.ProjectUserAdditionalPrivilege).insert(chunk).onConflict("id").merge(); await knex(TableName.ProjectUserAdditionalPrivilege).insert(chunk).onConflict("id").merge();
} }
} }
@@ -203,14 +203,14 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "GET", method: "GET",
url: "/:projectSlug/roles/slug/:slug", url: "/:projectSlug/roles/slug/:roleSlug",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
schema: { schema: {
params: z.object({ params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug), projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
slug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug) roleSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -226,7 +226,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug, projectSlug: req.params.projectSlug,
roleSlug: req.params.slug roleSlug: req.params.roleSlug
}); });
return { role }; return { role };
} }
@@ -1,36 +0,0 @@
import { ForbiddenError, subject } from "@casl/ability";
import { ActorType } from "@app/services/auth/auth-type";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TIsApproversValid } from "./access-approval-policy-types";
export const isApproversValid = async ({
userIds,
projectId,
orgId,
envSlug,
actorAuthMethod,
secretPath,
permissionService
}: TIsApproversValid) => {
try {
for await (const userId of userIds) {
const { permission: approverPermission } = await permissionService.getProjectPermission(
ActorType.USER,
userId,
projectId,
actorAuthMethod,
orgId
);
ForbiddenError.from(approverPermission).throwUnlessCan(
ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment: envSlug, secretPath, secretName: "", secretTags: [] })
);
}
} catch {
return false;
}
return true;
};
@@ -41,7 +41,6 @@ type TDynamicSecretLeaseServiceFactoryDep = {
export type TDynamicSecretLeaseServiceFactory = ReturnType<typeof dynamicSecretLeaseServiceFactory>; export type TDynamicSecretLeaseServiceFactory = ReturnType<typeof dynamicSecretLeaseServiceFactory>;
// TODO(casl): change the lease to different permission
export const dynamicSecretLeaseServiceFactory = ({ export const dynamicSecretLeaseServiceFactory = ({
dynamicSecretLeaseDAL, dynamicSecretLeaseDAL,
dynamicSecretProviders, dynamicSecretProviders,
@@ -32,8 +32,6 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeServiceFactory typeof identityProjectAdditionalPrivilegeServiceFactory
>; >;
// TODO(akhilmhdh): move this to more centralized
const unpackPermissions = (permissions: unknown) => const unpackPermissions = (permissions: unknown) =>
UnpackedPermissionSchema.array().parse( UnpackedPermissionSchema.array().parse(
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]) unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
@@ -142,7 +142,7 @@ const CASL_ACTION_SCHEMA_NATIVE_ENUM = <ACTION extends z.EnumLike>(actions: ACTI
const CASL_ACTION_SCHEMA_ENUM = <ACTION extends z.EnumValues>(actions: ACTION) => const CASL_ACTION_SCHEMA_ENUM = <ACTION extends z.EnumValues>(actions: ACTION) =>
z.union([z.enum(actions), z.enum(actions).array().min(1)]).transform((el) => (typeof el === "string" ? [el] : el)); z.union([z.enum(actions), z.enum(actions).array().min(1)]).transform((el) => (typeof el === "string" ? [el] : el));
// akhilmhdh: don't mondify this for v2 // akhilmhdh: don't modify this for v2
// if you want to update create a new schema // if you want to update create a new schema
const SecretConditionV1Schema = z const SecretConditionV1Schema = z
.object({ .object({
@@ -339,6 +339,13 @@ const GeneralPermissionSchema = [
action: CASL_ACTION_SCHEMA_ENUM([ProjectPermissionActions.Edit]).describe( action: CASL_ACTION_SCHEMA_ENUM([ProjectPermissionActions.Edit]).describe(
"Describe what action an entity can take." "Describe what action an entity can take."
) )
}),
z.object({
subject: z.literal(ProjectPermissionSub.Cmek).describe("The entity this permission pertains to."),
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCmekActions).describe(
"Describe what action an entity can take."
)
}) })
]; ];
@@ -360,13 +367,6 @@ export const ProjectPermissionV1Schema = z.discriminatedUnion("subject", [
"Describe what action an entity can take." "Describe what action an entity can take."
) )
}), }),
z.object({
subject: z.literal(ProjectPermissionSub.Cmek).describe("The entity this permission pertains to."),
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCmekActions).describe(
"Describe what action an entity can take."
)
}),
...GeneralPermissionSchema ...GeneralPermissionSchema
]); ]);
@@ -74,7 +74,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
slug, slug,
permissions: customPermission permissions: customPermission
}); });
return additionalPrivilege; return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
} }
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange); const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
@@ -43,7 +43,7 @@ import {
fnSecretBulkDelete as fnSecretV2BridgeBulkDelete, fnSecretBulkDelete as fnSecretV2BridgeBulkDelete,
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert, fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate, fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllSecretReferences as getAllNestedSecretReferencesV2Bridge getAllSecretReferences as getAllSecretReferencesV2Bridge
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns"; } from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal"; import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
@@ -523,7 +523,7 @@ export const secretApprovalRequestServiceFactory = ({
skipMultilineEncoding: el.skipMultilineEncoding, skipMultilineEncoding: el.skipMultilineEncoding,
key: el.key, key: el.key,
references: el.encryptedValue references: el.encryptedValue
? getAllNestedSecretReferencesV2Bridge( ? getAllSecretReferencesV2Bridge(
secretManagerDecryptor({ secretManagerDecryptor({
cipherTextBlob: el.encryptedValue cipherTextBlob: el.encryptedValue
}).toString() }).toString()
@@ -547,7 +547,7 @@ export const secretApprovalRequestServiceFactory = ({
? { ? {
encryptedValue: el.encryptedValue as Buffer, encryptedValue: el.encryptedValue as Buffer,
references: el.encryptedValue references: el.encryptedValue
? getAllNestedSecretReferencesV2Bridge( ? getAllSecretReferencesV2Bridge(
secretManagerDecryptor({ secretManagerDecryptor({
cipherTextBlob: el.encryptedValue cipherTextBlob: el.encryptedValue
}).toString() }).toString()
@@ -28,7 +28,6 @@ import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret
import { import {
fnSecretBulkInsert as fnSecretV2BridgeBulkInsert, fnSecretBulkInsert as fnSecretV2BridgeBulkInsert,
fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate, fnSecretBulkUpdate as fnSecretV2BridgeBulkUpdate,
getAllSecretReferences as getAllNestedSecretReferencesV2Bridge,
getAllSecretReferences getAllSecretReferences
} from "@app/services/secret-v2-bridge/secret-v2-bridge-fns"; } from "@app/services/secret-v2-bridge/secret-v2-bridge-fns";
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal"; import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
@@ -258,7 +257,6 @@ export const secretReplicationServiceFactory = ({
folderDAL, folderDAL,
secretImportDAL, secretImportDAL,
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""), decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""),
// TODO(casl): check with team
hasSecretAccess: () => true hasSecretAccess: () => true
}); });
// secrets that gets replicated across imports // secrets that gets replicated across imports
@@ -418,9 +416,7 @@ export const secretReplicationServiceFactory = ({
encryptedValue: doc.encryptedValue, encryptedValue: doc.encryptedValue,
encryptedComment: doc.encryptedComment, encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding, skipMultilineEncoding: doc.skipMultilineEncoding,
references: doc.secretValue references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
? getAllNestedSecretReferencesV2Bridge(doc.secretValue).nestedReferences
: []
}; };
}) })
}); });
@@ -446,9 +442,7 @@ export const secretReplicationServiceFactory = ({
encryptedValue: doc.encryptedValue as Buffer, encryptedValue: doc.encryptedValue as Buffer,
encryptedComment: doc.encryptedComment, encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding, skipMultilineEncoding: doc.skipMultilineEncoding,
references: doc.secretValue references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
? getAllNestedSecretReferencesV2Bridge(doc.secretValue).nestedReferences
: []
} }
}; };
}) })
+22
View File
@@ -81,3 +81,25 @@ export const chunkArray = <T>(array: T[], chunkSize: number): T[][] => {
} }
return chunks; return chunks;
}; };
/*
* Returns all items from the first list that
* do not exist in the second list.
*/
export const diff = <T>(
root: readonly T[],
other: readonly T[],
identity: (item: T) => string | number | symbol = (t: T) => t as unknown as string | number | symbol
): T[] => {
if (!root?.length && !other?.length) return [];
if (root?.length === undefined) return [...other];
if (!other?.length) return [...root];
const bKeys = other.reduce(
(acc, item) => {
acc[identity(item)] = true;
return acc;
},
{} as Record<string | number | symbol, boolean>
);
return root.filter((a) => !bKeys[identity(a)]);
};
-4
View File
@@ -21,10 +21,6 @@ type TKnexGroupOperator<T extends object> = {
value: (TKnexNonGroupOperator<T> | TKnexGroupOperator<T>)[]; value: (TKnexNonGroupOperator<T> | TKnexGroupOperator<T>)[];
}; };
// akhilmhdh: This is still in pending state and not yet ready. If you want to use it ping me.
// used when you need to write a complex query with the orm
// use it when you need complex or and and condition - most of the time not needed
// majorly used with casl permission to filter data based on permission
export type TKnexDynamicOperator<T extends object> = TKnexGroupOperator<T> | TKnexNonGroupOperator<T>; export type TKnexDynamicOperator<T extends object> = TKnexGroupOperator<T> | TKnexNonGroupOperator<T>;
export const buildDynamicKnexQuery = <T extends object>( export const buildDynamicKnexQuery = <T extends object>(
@@ -195,7 +195,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
req.permission.orgId req.permission.orgId
); );
const allowedDynamicSecretEnviroments = // filter envs user has access to const allowedDynamicSecretEnvironments = // filter envs user has access to
environments.filter((environment) => environments.filter((environment) =>
permission.can( permission.can(
ProjectPermissionDynamicSecretActions.Lease, ProjectPermissionDynamicSecretActions.Lease,
@@ -203,7 +203,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
) )
); );
if (includeDynamicSecrets && allowedDynamicSecretEnviroments.length) { if (includeDynamicSecrets && allowedDynamicSecretEnvironments.length) {
// this is the unique count, ie duplicate secrets across envs only count as 1 // this is the unique count, ie duplicate secrets across envs only count as 1
totalDynamicSecretCount = await server.services.dynamicSecret.getCountMultiEnv({ totalDynamicSecretCount = await server.services.dynamicSecret.getCountMultiEnv({
actor: req.permission.type, actor: req.permission.type,
@@ -212,7 +212,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId, projectId,
search, search,
environmentSlugs: allowedDynamicSecretEnviroments, environmentSlugs: allowedDynamicSecretEnvironments,
path: secretPath, path: secretPath,
isInternal: true isInternal: true
}); });
@@ -227,7 +227,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
search, search,
orderBy, orderBy,
orderDirection, orderDirection,
environmentSlugs: allowedDynamicSecretEnviroments, environmentSlugs: allowedDynamicSecretEnvironments,
path: secretPath, path: secretPath,
limit: remainingLimit, limit: remainingLimit,
offset: adjustedOffset, offset: adjustedOffset,
@@ -543,9 +543,18 @@ export const secretImportServiceFactory = ({
// this will already order by position // this will already order by position
// so anything based on this order will also be in right position // so anything based on this order will also be in right position
const secretImports = await secretImportDAL.find({ folderId: folder.id, isReplication: false }); const secretImports = await secretImportDAL.find({ folderId: folder.id, isReplication: false });
const allowedImports = secretImports.filter((el) =>
// TODO(casl): update here permission.can(
return fnSecretsFromImports({ allowedImports: secretImports, folderDAL, secretDAL, secretImportDAL }); ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {
environment: el.importEnv.slug,
secretPath: el.importPath,
secretName: "",
secretTags: []
})
)
);
return fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL });
}; };
const getRawSecretsFromImports = async ({ const getRawSecretsFromImports = async ({
@@ -606,8 +615,19 @@ export const secretImportServiceFactory = ({
name: "bot_not_found_error" name: "bot_not_found_error"
}); });
const allowedImports = secretImports.filter((el) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, {
environment: el.importEnv.slug,
secretPath: el.importPath,
secretName: "",
secretTags: []
})
)
);
const importedSecrets = await fnSecretsFromImports({ const importedSecrets = await fnSecretsFromImports({
allowedImports: secretImports, allowedImports,
folderDAL, folderDAL,
secretDAL, secretDAL,
secretImportDAL secretImportDAL
@@ -107,7 +107,7 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
}); });
return data; return data;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: `${TableName.SecretV2}: FindOne` }); throw new DatabaseError({ error, name: `${TableName.SecretV2}: Find` });
} }
}; };
@@ -434,24 +434,24 @@ export const expandSecretReferencesFactory = ({
const [secretKey] = entities; const [secretKey] = entities;
// eslint-disable-next-line no-continue,no-await-in-loop // eslint-disable-next-line no-continue,no-await-in-loop
const referedValue = await fetchSecret(environment, secretPath, secretKey); const referredValue = await fetchSecret(environment, secretPath, secretKey);
if (!canExpandValue(environment, secretPath, secretKey, referedValue.tags)) if (!canExpandValue(environment, secretPath, secretKey, referredValue.tags))
throw new ForbiddenRequestError({ throw new ForbiddenRequestError({
message: `You are attempting to reference secret named ${secretKey} from environment ${environment} in path ${secretPath} which you do not have access to.` message: `You are attempting to reference secret named ${secretKey} from environment ${environment} in path ${secretPath} which you do not have access to.`
}); });
const cacheKey = getCacheUniqueKey(environment, secretPath); const cacheKey = getCacheUniqueKey(environment, secretPath);
secretCache[cacheKey][secretKey] = referedValue; secretCache[cacheKey][secretKey] = referredValue;
if (INTERPOLATION_SYNTAX_REG.test(referedValue.value)) { if (INTERPOLATION_SYNTAX_REG.test(referredValue.value)) {
stack.push({ stack.push({
value: referedValue.value, value: referredValue.value,
secretPath, secretPath,
environment, environment,
depth: depth + 1 depth: depth + 1
}); });
} }
if (referedValue) { if (referredValue) {
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue.value); expandedValue = expandedValue.replaceAll(interpolationSyntax, referredValue.value);
} }
} else { } else {
const secretReferenceEnvironment = entities[0]; const secretReferenceEnvironment = entities[0];
@@ -9,7 +9,7 @@ import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approv
import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal"; import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn"; import { diff, groupBy } from "@app/lib/fn";
import { setKnexStringValue } from "@app/lib/knex"; import { setKnexStringValue } from "@app/lib/knex";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -105,7 +105,12 @@ export const secretV2BridgeServiceFactory = ({
const uniqueReferenceEnvironmentSlugs = Array.from(new Set(references.map((el) => el.environment))); const uniqueReferenceEnvironmentSlugs = Array.from(new Set(references.map((el) => el.environment)));
const referencesEnvironments = await projectEnvDAL.findBySlugs(projectId, uniqueReferenceEnvironmentSlugs); const referencesEnvironments = await projectEnvDAL.findBySlugs(projectId, uniqueReferenceEnvironmentSlugs);
if (referencesEnvironments.length !== uniqueReferenceEnvironmentSlugs.length) if (referencesEnvironments.length !== uniqueReferenceEnvironmentSlugs.length)
throw new BadRequestError({ message: "Referred environment not found" }); throw new BadRequestError({
message: `Referenced environment not found. Missing ${diff(
uniqueReferenceEnvironmentSlugs,
referencesEnvironments.map((el) => el.slug)
).join(",")}`
});
const referencesEnvironmentGroupBySlug = groupBy(referencesEnvironments, (i) => i.slug); const referencesEnvironmentGroupBySlug = groupBy(referencesEnvironments, (i) => i.slug);
const referredFolders = await folderDAL.findByManySecretPath( const referredFolders = await folderDAL.findByManySecretPath(
@@ -122,7 +127,7 @@ export const secretV2BridgeServiceFactory = ({
const folderId = const folderId =
referencesFolderGroupByPath[`${referencesEnvironmentGroupBySlug[el.environment][0].id}-${el.secretPath}`][0] referencesFolderGroupByPath[`${referencesEnvironmentGroupBySlug[el.environment][0].id}-${el.secretPath}`][0]
?.id; ?.id;
if (!folderId) throw new BadRequestError({ message: `Reference path ${el.secretPath} doesn't exist` }); if (!folderId) throw new BadRequestError({ message: `Referenced path ${el.secretPath} doesn't exist` });
return { return {
operator: "and", operator: "and",
@@ -144,7 +149,12 @@ export const secretV2BridgeServiceFactory = ({
}); });
if (referredSecrets.length !== references.length) if (referredSecrets.length !== references.length)
throw new BadRequestError({ message: "Reference secret not found" }); throw new BadRequestError({
message: `Referenced secret not found. Found only ${diff(
references.map((el) => el.secretKey),
referredSecrets.map((el) => el.key)
).join(",")}`
});
const referredSecretsGroupBySecretKey = groupBy(referredSecrets, (i) => i.key); const referredSecretsGroupBySecretKey = groupBy(referredSecrets, (i) => i.key);
references.forEach((el) => { references.forEach((el) => {
@@ -210,7 +220,8 @@ export const secretV2BridgeServiceFactory = ({
// validate tags // validate tags
// fetch all tags and if not same count throw error meaning one was invalid tags // fetch all tags and if not same count throw error meaning one was invalid tags
const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : []; const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : [];
if ((inputSecret.tagIds || []).length !== tags.length) throw new NotFoundError({ message: "Tag not found" }); if ((inputSecret.tagIds || []).length !== tags.length)
throw new NotFoundError({ message: `Tag not found. Found ${tags.map((el) => el.slug).join(",")}` });
const { secretName, type, ...inputSecretData } = inputSecret; const { secretName, type, ...inputSecretData } = inputSecret;
@@ -357,7 +368,8 @@ export const secretV2BridgeServiceFactory = ({
// validate tags // validate tags
// fetch all tags and if not same count throw error meaning one was invalid tags // fetch all tags and if not same count throw error meaning one was invalid tags
const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : []; const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : [];
if ((inputSecret.tagIds || []).length !== tags.length) throw new NotFoundError({ message: "Tag not found" }); if ((inputSecret.tagIds || []).length !== tags.length)
throw new NotFoundError({ message: `Tag not found. Found ${tags.map((el) => el.slug).join(",")}` });
// now check with new ids // now check with new ids
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
@@ -505,7 +517,7 @@ export const secretV2BridgeServiceFactory = ({
}); });
if (!secretToDelete) throw new NotFoundError({ message: "Secret not found" }); if (!secretToDelete) throw new NotFoundError({ message: "Secret not found" });
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, ProjectPermissionActions.Delete,
subject(ProjectPermissionSub.Secrets, { subject(ProjectPermissionSub.Secrets, {
environment, environment,
secretPath, secretPath,
@@ -1088,7 +1100,8 @@ export const secretV2BridgeServiceFactory = ({
// get all tags // get all tags
const sanitizedTagIds = inputSecrets.flatMap(({ tagIds = [] }) => tagIds); const sanitizedTagIds = inputSecrets.flatMap(({ tagIds = [] }) => tagIds);
const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : []; const tags = sanitizedTagIds.length ? await secretTagDAL.findManyTagsById(projectId, sanitizedTagIds) : [];
if (tags.length !== sanitizedTagIds.length) throw new NotFoundError({ message: "Tag not found" }); if (tags.length !== sanitizedTagIds.length)
throw new NotFoundError({ message: `Tag not found. Found ${tags.map((el) => el.slug).join(",")}` });
const tagsGroupByID = groupBy(tags, (i) => i.id); const tagsGroupByID = groupBy(tags, (i) => i.id);
inputSecrets.forEach((el) => { inputSecrets.forEach((el) => {