mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 09:28:06 +00:00
feat: addressed backend review changes needed by scott
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
-2
@@ -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
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -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
|
|
||||||
: []
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)]);
|
||||||
|
};
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user