diff --git a/backend/e2e-test/routes/v2/service-token.spec.ts b/backend/e2e-test/routes/v2/service-token.spec.ts index bfe0435230..6cc8f6e34f 100644 --- a/backend/e2e-test/routes/v2/service-token.spec.ts +++ b/backend/e2e-test/routes/v2/service-token.spec.ts @@ -6,7 +6,7 @@ import { decryptAsymmetric, decryptSymmetric128BitHexKeyUTF8, encryptSymmetric12 const createServiceToken = async ( scopes: { environment: string; secretPath: string }[], - permissions: ("read" | "write" | "readValue")[] + permissions: ("read" | "write")[] ) => { const projectKeyRes = await testServer.inject({ method: "GET", @@ -139,7 +139,7 @@ describe("Service token secret ops", async () => { beforeAll(async () => { serviceToken = await createServiceToken( [{ secretPath: "/**", environment: seedData1.environment.slug }], - ["read", "write", "readValue"] + ["read", "write"] ); // this is ensure cli service token decryptiong working fine @@ -496,7 +496,7 @@ describe("Service token fail cases", async () => { test("Unauthorized secret path access", async () => { const serviceToken = await createServiceToken( [{ secretPath: "/", environment: seedData1.environment.slug }], - ["read", "readValue", "write"] + ["read", "write"] ); const fetchSecrets = await testServer.inject({ method: "GET", @@ -518,7 +518,7 @@ describe("Service token fail cases", async () => { test("Unauthorized secret environment access", async () => { const serviceToken = await createServiceToken( [{ secretPath: "/", environment: seedData1.environment.slug }], - ["read", "readValue", "write"] + ["read", "write"] ); const fetchSecrets = await testServer.inject({ method: "GET", @@ -540,7 +540,7 @@ describe("Service token fail cases", async () => { test("Unauthorized write operation", async () => { const serviceToken = await createServiceToken( [{ secretPath: "/", environment: seedData1.environment.slug }], - ["read", "readValue"] + ["read"] ); const writeSecrets = await testServer.inject({ method: "POST", diff --git a/backend/e2e-test/vitest-environment-knex.ts b/backend/e2e-test/vitest-environment-knex.ts index 46b3223492..9dfb38aeb9 100644 --- a/backend/e2e-test/vitest-environment-knex.ts +++ b/backend/e2e-test/vitest-environment-knex.ts @@ -120,3 +120,4 @@ export default { }; } }; + diff --git a/backend/src/db/migrations/20250218020306_backfill-secret-permissions-with-readvalue.ts b/backend/src/db/migrations/20250218020306_backfill-secret-permissions-with-readvalue.ts deleted file mode 100644 index 9e567cefe0..0000000000 --- a/backend/src/db/migrations/20250218020306_backfill-secret-permissions-with-readvalue.ts +++ /dev/null @@ -1,313 +0,0 @@ -import { MongoAbility, RawRuleOf } from "@casl/ability"; -import { PackRule, packRules, unpackRules } from "@casl/ability/extra"; -import { Knex } from "knex"; -import { z } from "zod"; - -import { selectAllTableCols } from "@app/lib/knex"; - -import { TableName } from "../schemas"; - -enum ProjectPermissionSub { - Secrets = "secrets" -} - -enum SecretActions { - Read = "read", - ReadValue = "readValue" -} - -const UnpackedPermissionSchema = z.object({ - subject: z - .union([z.string().min(1), z.string().array()]) - .transform((el) => (typeof el !== "string" ? el[0] : el)) - .optional(), - action: z.union([z.string().min(1), z.string().array()]).transform((el) => (typeof el === "string" ? [el] : el)), - conditions: z.unknown().optional(), - inverted: z.boolean().optional() -}); - -const $unpackPermissions = (permissions: unknown) => - UnpackedPermissionSchema.array().parse(unpackRules((permissions || []) as PackRule>[])); - -const $updatePermissionsUp = (permissions: unknown) => { - const parsedPermissions = $unpackPermissions(permissions); - let shouldUpdate = false; - - for (let i = 0; i < parsedPermissions.length; i += 1) { - const parsedPermission = parsedPermissions[i]; - const { subject, action } = parsedPermission; - - if (subject === ProjectPermissionSub.Secrets) { - if (action.includes(SecretActions.Read) && !action.includes(SecretActions.ReadValue)) { - action.push(SecretActions.ReadValue); - parsedPermissions[i] = { ...parsedPermission, action }; - shouldUpdate = true; - } - } - } - - return { - parsedPermissions, - shouldUpdate - }; -}; - -const $updatePermissionsDown = (permissions: unknown) => { - const parsedPermissions = $unpackPermissions(permissions); - - let shouldUpdate = false; - for (let i = 0; i < parsedPermissions.length; i += 1) { - const parsedPermission = parsedPermissions[i]; - - const { subject, action } = parsedPermission; - - if (subject === ProjectPermissionSub.Secrets) { - const readValueIndex = action.indexOf(SecretActions.ReadValue); - - if (action.includes(SecretActions.ReadValue) && readValueIndex !== -1) { - action.splice(readValueIndex, 1); - parsedPermissions[i] = { ...parsedPermission, action }; - - shouldUpdate = true; - } - } - } - - const repackedPermissions = packRules(parsedPermissions); - - return { - repackedPermissions, - shouldUpdate - }; -}; - -const CHUNK_SIZE = 1000; - -export async function up(knex: Knex): Promise { - const projectRoles = await knex(TableName.ProjectRoles).select(selectAllTableCols(TableName.ProjectRoles)); - const projectIdentityAdditionalPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select( - selectAllTableCols(TableName.IdentityProjectAdditionalPrivilege) - ); - const projectUserAdditionalPrivileges = await knex(TableName.ProjectUserAdditionalPrivilege).select( - selectAllTableCols(TableName.ProjectUserAdditionalPrivilege) - ); - - const serviceTokens = await knex(TableName.ServiceToken).select(selectAllTableCols(TableName.ServiceToken)); - - const updatedServiceTokens = serviceTokens.reduce((acc, serviceToken) => { - const { permissions } = serviceToken; // Service tokens are special, and include an array of actions only. - - if (permissions.includes(SecretActions.Read) && !permissions.includes(SecretActions.ReadValue)) { - permissions.push(SecretActions.ReadValue); - acc.push({ - ...serviceToken, - permissions - }); - } - return acc; - }, []); - - if (updatedServiceTokens.length > 0) { - for (let i = 0; i < updatedServiceTokens.length; i += CHUNK_SIZE) { - const chunk = updatedServiceTokens.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ServiceToken) - .whereIn( - "id", - chunk.map((t) => t.id) - ) - .update({ - // @ts-expect-error -- raw query - permissions: knex.raw( - `CASE id - ${chunk.map((t) => `WHEN '${t.id}' THEN ?::text[]`).join(" ")} - END`, - chunk.map((t) => t.permissions) - ) - }); - } - } - - const updatedRoles = projectRoles.reduce((acc, projectRole) => { - const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(projectRole.permissions); - - if (shouldUpdate) { - acc.push({ - ...projectRole, - permissions: JSON.stringify(packRules(parsedPermissions)) - }); - } - return acc; - }, []); - - const updatedIdentityAdditionalPrivileges = projectIdentityAdditionalPrivileges.reduce< - typeof projectIdentityAdditionalPrivileges - >((acc, identityAdditionalPrivilege) => { - const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(identityAdditionalPrivilege.permissions); - - if (shouldUpdate) { - acc.push({ - ...identityAdditionalPrivilege, - permissions: JSON.stringify(packRules(parsedPermissions)) - }); - } - return acc; - }, []); - - const updatedUserAdditionalPrivileges = projectUserAdditionalPrivileges.reduce< - typeof projectUserAdditionalPrivileges - >((acc, userAdditionalPrivilege) => { - const { shouldUpdate, parsedPermissions } = $updatePermissionsUp(userAdditionalPrivilege.permissions); - - if (shouldUpdate) { - acc.push({ - ...userAdditionalPrivilege, - permissions: JSON.stringify(packRules(parsedPermissions)) - }); - } - return acc; - }, []); - - if (updatedRoles.length > 0) { - for (let i = 0; i < updatedRoles.length; i += CHUNK_SIZE) { - const chunk = updatedRoles.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ProjectRoles).insert(chunk).onConflict("id").merge(["permissions"]); - } - } - - if (updatedIdentityAdditionalPrivileges.length > 0) { - for (let i = 0; i < updatedIdentityAdditionalPrivileges.length; i += CHUNK_SIZE) { - const chunk = updatedIdentityAdditionalPrivileges.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.IdentityProjectAdditionalPrivilege).insert(chunk).onConflict("id").merge(["permissions"]); - } - } - - if (updatedUserAdditionalPrivileges.length > 0) { - for (let i = 0; i < updatedUserAdditionalPrivileges.length; i += CHUNK_SIZE) { - const chunk = updatedUserAdditionalPrivileges.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ProjectUserAdditionalPrivilege).insert(chunk).onConflict("id").merge(["permissions"]); - } - } -} - -export async function down(knex: Knex): Promise { - const projectRoles = await knex(TableName.ProjectRoles).select(selectAllTableCols(TableName.ProjectRoles)); - const identityAdditionalPrivileges = await knex(TableName.IdentityProjectAdditionalPrivilege).select( - selectAllTableCols(TableName.IdentityProjectAdditionalPrivilege) - ); - const userAdditionalPrivileges = await knex(TableName.ProjectUserAdditionalPrivilege).select( - selectAllTableCols(TableName.ProjectUserAdditionalPrivilege) - ); - const serviceTokens = await knex(TableName.ServiceToken).select(selectAllTableCols(TableName.ServiceToken)); - - const updatedServiceTokens = serviceTokens.reduce((acc, serviceToken) => { - const { permissions } = serviceToken; - - if (permissions.includes(SecretActions.ReadValue)) { - permissions.splice(permissions.indexOf(SecretActions.ReadValue), 1); - acc.push({ - ...serviceToken, - permissions - }); - } - return acc; - }, []); - - if (updatedServiceTokens.length > 0) { - for (let i = 0; i < updatedServiceTokens.length; i += CHUNK_SIZE) { - const chunk = updatedServiceTokens.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ServiceToken) - .whereIn( - "id", - chunk.map((t) => t.id) - ) - .update({ - // @ts-expect-error -- raw query - permissions: knex.raw( - `CASE id - ${chunk.map((t) => `WHEN '${t.id}' THEN ?::text[]`).join(" ")} - END`, - chunk.map((t) => t.permissions) - ) - }); - } - } - - const updatedRoles = projectRoles.reduce((acc, projectRole) => { - const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(projectRole.permissions); - - if (shouldUpdate) { - acc.push({ - ...projectRole, - permissions: JSON.stringify(repackedPermissions) - }); - } - return acc; - }, []); - - const updatedIdentityAdditionalPrivileges = identityAdditionalPrivileges.reduce( - (acc, identityAdditionalPrivilege) => { - const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(identityAdditionalPrivilege.permissions); - - if (shouldUpdate) { - acc.push({ - ...identityAdditionalPrivilege, - permissions: JSON.stringify(repackedPermissions) - }); - } - return acc; - }, - [] - ); - - const updatedUserAdditionalPrivileges = userAdditionalPrivileges.reduce( - (acc, userAdditionalPrivilege) => { - const { shouldUpdate, repackedPermissions } = $updatePermissionsDown(userAdditionalPrivilege.permissions); - - if (shouldUpdate) { - acc.push({ - ...userAdditionalPrivilege, - permissions: JSON.stringify(repackedPermissions) - }); - } - return acc; - }, - [] - ); - - if (updatedRoles.length > 0) { - for (let i = 0; i < updatedRoles.length; i += CHUNK_SIZE) { - const chunk = updatedRoles.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ProjectRoles).insert(chunk).onConflict("id").merge(["permissions"]); - } - } - - if (updatedIdentityAdditionalPrivileges.length > 0) { - for (let i = 0; i < updatedIdentityAdditionalPrivileges.length; i += CHUNK_SIZE) { - const chunk = updatedIdentityAdditionalPrivileges.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.IdentityProjectAdditionalPrivilege).insert(chunk).onConflict("id").merge(["permissions"]); - } - } - - if (updatedUserAdditionalPrivileges.length > 0) { - for (let i = 0; i < updatedUserAdditionalPrivileges.length; i += CHUNK_SIZE) { - const chunk = updatedUserAdditionalPrivileges.slice(i, i + CHUNK_SIZE); - - // eslint-disable-next-line no-await-in-loop - await knex(TableName.ProjectUserAdditionalPrivilege).insert(chunk).onConflict("id").merge(["permissions"]); - } - } -} diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index 1ceb6019b8..653d04d4f3 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -1,11 +1,16 @@ import { z } from "zod"; -import { SecretApprovalRequestsReviewersSchema, SecretApprovalRequestsSchema, UsersSchema } from "@app/db/schemas"; +import { + SecretApprovalRequestsReviewersSchema, + SecretApprovalRequestsSchema, + SecretTagsSchema, + UsersSchema +} from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas"; +import { secretRawSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema"; @@ -245,6 +250,14 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv } }); + const tagSchema = SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .array() + .optional(); + server.route({ method: "GET", url: "/:id", @@ -278,7 +291,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv .omit({ _id: true, environment: true, workspace: true, type: true, version: true }) .extend({ op: z.string(), - tags: SanitizedTagSchema.array().optional(), + tags: tagSchema, secretMetadata: ResourceMetadataSchema.nullish(), secret: z .object({ @@ -297,7 +310,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv secretKey: z.string(), secretValue: z.string().optional(), secretComment: z.string().optional(), - tags: SanitizedTagSchema.array().optional(), + tags: tagSchema, secretMetadata: ResourceMetadataSchema.nullish() }) .optional() diff --git a/backend/src/ee/routes/v1/secret-router.ts b/backend/src/ee/routes/v1/secret-router.ts index a964eb1b85..4c249afe04 100644 --- a/backend/src/ee/routes/v1/secret-router.ts +++ b/backend/src/ee/routes/v1/secret-router.ts @@ -1,6 +1,6 @@ import z from "zod"; -import { ProjectPermissionSecretActions } from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions } from "@app/ee/services/permission/project-permission"; import { RAW_SECRETS } from "@app/lib/api-docs"; import { removeTrailingSlash } from "@app/lib/fn"; import { readLimit } from "@app/server/config/rateLimiter"; @@ -9,7 +9,7 @@ import { AuthMode } from "@app/services/auth/auth-type"; const AccessListEntrySchema = z .object({ - allowedActions: z.nativeEnum(ProjectPermissionSecretActions).array(), + allowedActions: z.nativeEnum(ProjectPermissionActions).array(), id: z.string(), membershipId: z.string(), name: z.string() diff --git a/backend/src/ee/routes/v1/secret-version-router.ts b/backend/src/ee/routes/v1/secret-version-router.ts index a09a05c91a..11443ebfee 100644 --- a/backend/src/ee/routes/v1/secret-version-router.ts +++ b/backend/src/ee/routes/v1/secret-version-router.ts @@ -22,11 +22,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) => }), response: { 200: z.object({ - secretVersions: secretRawSchema - .extend({ - secretValueHidden: z.boolean() - }) - .array() + secretVersions: secretRawSchema.array() }) } }, @@ -41,7 +37,6 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) => offset: req.query.offset, secretId: req.params.secretId }); - return { secretVersions }; } }); diff --git a/backend/src/ee/routes/v1/snapshot-router.ts b/backend/src/ee/routes/v1/snapshot-router.ts index 283b9b31e0..fc2d257129 100644 --- a/backend/src/ee/routes/v1/snapshot-router.ts +++ b/backend/src/ee/routes/v1/snapshot-router.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import { SecretSnapshotsSchema } from "@app/db/schemas"; +import { SecretSnapshotsSchema, SecretTagsSchema } from "@app/db/schemas"; import { PROJECTS } from "@app/lib/api-docs"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas"; +import { secretRawSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; export const registerSnapshotRouter = async (server: FastifyZodProvider) => { @@ -31,9 +31,12 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { secretVersions: secretRawSchema .omit({ _id: true, environment: true, workspace: true, type: true }) .extend({ - secretValueHidden: z.boolean(), secretId: z.string(), - tags: SanitizedTagSchema.array() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }).array() }) .array(), folderVersion: z.object({ id: z.string(), name: z.string() }).array(), @@ -52,7 +55,6 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { actorOrgId: req.permission.orgId, id: req.params.secretSnapshotId }); - return { secretSnapshot }; } }); diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index 3f69c481a2..4389c38666 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -17,14 +17,6 @@ export enum ProjectPermissionActions { Delete = "delete" } -export enum ProjectPermissionSecretActions { - DescribeSecret = "read", - ReadValue = "readValue", - Create = "create", - Edit = "edit", - Delete = "delete" -} - export enum ProjectPermissionCmekActions { Read = "read", Create = "create", @@ -123,7 +115,7 @@ export type IdentityManagementSubjectFields = { export type ProjectPermissionSet = | [ - ProjectPermissionSecretActions, + ProjectPermissionActions, ProjectPermissionSub.Secrets | (ForcedSubject & SecretSubjectFields) ] | [ @@ -437,7 +429,6 @@ const GeneralPermissionSchema = [ }) ]; -// Do not update this schema anymore, as it's kept purely for backwards compatability. Update V2 schema only. export const ProjectPermissionV1Schema = z.discriminatedUnion("subject", [ z.object({ subject: z.literal(ProjectPermissionSub.Secrets).describe("The entity this permission pertains to."), @@ -469,7 +460,7 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [ z.object({ subject: z.literal(ProjectPermissionSub.Secrets).describe("The entity this permission pertains to."), inverted: z.boolean().optional().describe("Whether rule allows or forbids."), - action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionSecretActions).describe( + action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( "Describe what action an entity can take." ), conditions: SecretConditionV2Schema.describe( @@ -526,6 +517,7 @@ const buildAdminPermissionRules = () => { // Admins get full access to everything [ + ProjectPermissionSub.Secrets, ProjectPermissionSub.SecretFolders, ProjectPermissionSub.SecretImports, ProjectPermissionSub.SecretApproval, @@ -558,21 +550,10 @@ const buildAdminPermissionRules = () => { ProjectPermissionActions.Create, ProjectPermissionActions.Delete ], - el + el as ProjectPermissionSub ); }); - can( - [ - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSecretActions.ReadValue, - ProjectPermissionSecretActions.Create, - ProjectPermissionSecretActions.Edit, - ProjectPermissionSecretActions.Delete - ], - ProjectPermissionSub.Secrets - ); - can( [ ProjectPermissionDynamicSecretActions.ReadRootCredential, @@ -632,11 +613,10 @@ const buildMemberPermissionRules = () => { can( [ - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSecretActions.ReadValue, - ProjectPermissionSecretActions.Edit, - ProjectPermissionSecretActions.Create, - ProjectPermissionSecretActions.Delete + ProjectPermissionActions.Read, + ProjectPermissionActions.Edit, + ProjectPermissionActions.Create, + ProjectPermissionActions.Delete ], ProjectPermissionSub.Secrets ); @@ -808,8 +788,7 @@ export const projectMemberPermissions = buildMemberPermissionRules(); const buildViewerPermissionRules = () => { const { can, rules } = new AbilityBuilder>(createMongoAbility); - can(ProjectPermissionSecretActions.DescribeSecret, ProjectPermissionSub.Secrets); - can(ProjectPermissionSecretActions.ReadValue, ProjectPermissionSub.Secrets); + can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretFolders); can(ProjectPermissionDynamicSecretActions.ReadRootCredential, ProjectPermissionSub.DynamicSecrets); can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports); @@ -852,8 +831,6 @@ export const buildServiceTokenProjectPermission = ( ) => { const canWrite = permission.includes("write"); const canRead = permission.includes("read"); - const canReadValue = permission.includes("readValue"); - const { can, build } = new AbilityBuilder>(createMongoAbility); scopes.forEach(({ secretPath, environment }) => { [ProjectPermissionSub.Secrets, ProjectPermissionSub.SecretImports, ProjectPermissionSub.SecretFolders].forEach( @@ -883,14 +860,6 @@ export const buildServiceTokenProjectPermission = ( environment }); } - - if (subject === ProjectPermissionSub.Secrets && canReadValue) { - // @ts-expect-error type - can(ProjectPermissionSecretActions.ReadValue, subject as ProjectPermissionSub.Secrets, { - secretPath: { $glob: secretPath }, - environment - }); - } } ); }); @@ -947,17 +916,7 @@ export const backfillPermissionV1SchemaToV2Schema = ( subject: ProjectPermissionSub.SecretImports as const })); - const secretPolicies = secretSubjects.map(({ subject, ...el }) => ({ - subject: ProjectPermissionSub.Secrets as const, - ...el, - action: - el.action.includes(ProjectPermissionActions.Read) && !el.action.includes(ProjectPermissionSecretActions.ReadValue) - ? el.action.concat(ProjectPermissionSecretActions.ReadValue) - : el.action - })); - const secretFolderPolicies = secretSubjects - .map(({ subject, ...el }) => ({ ...el, // read permission is not needed anymore @@ -999,7 +958,6 @@ export const backfillPermissionV1SchemaToV2Schema = ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore-error this is valid ts secretImportPolicies, - secretPolicies, dynamicSecretPolicies, hasReadOnlyFolder.length ? [] : secretFolderPolicies ); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 5430558a91..8569ef2a9c 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -58,7 +58,7 @@ import { TUserDALFactory } from "@app/services/user/user-dal"; import { TLicenseServiceFactory } from "../license/license-service"; import { TPermissionServiceFactory } from "../permission/permission-service"; -import { ProjectPermissionSecretActions, ProjectPermissionSub } from "../permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal"; import { TSecretSnapshotServiceFactory } from "../secret-snapshot/secret-snapshot-service"; import { TSecretApprovalRequestDALFactory } from "./secret-approval-request-dal"; @@ -88,12 +88,7 @@ type TSecretApprovalRequestServiceFactoryDep = { secretDAL: TSecretDALFactory; secretTagDAL: Pick< TSecretTagDALFactory, - | "findManyTagsById" - | "saveTagsToSecret" - | "deleteTagsManySecret" - | "saveTagsToSecretV2" - | "deleteTagsToSecretV2" - | "find" + "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret" | "saveTagsToSecretV2" | "deleteTagsToSecretV2" >; secretBlindIndexDAL: Pick; snapshotService: Pick; @@ -111,7 +106,7 @@ type TSecretApprovalRequestServiceFactoryDep = { kmsService: Pick; secretV2BridgeDAL: Pick< TSecretV2BridgeDALFactory, - "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find" + "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" >; secretVersionV2BridgeDAL: Pick; secretVersionTagV2BridgeDAL: Pick; @@ -919,7 +914,7 @@ export const secretApprovalRequestServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); @@ -1006,7 +1001,6 @@ export const secretApprovalRequestServiceFactory = ({ : keyName2BlindIndex[secretName]; // add tags if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds; - return { ...latestSecretVersions[secretId], ...el, @@ -1369,9 +1363,9 @@ export const secretApprovalRequestServiceFactory = ({ const tagsGroupById = groupBy(tags, (i) => i.id); commits.forEach((commit) => { - let action = ProjectPermissionSecretActions.Create; - if (commit.op === SecretOperations.Update) action = ProjectPermissionSecretActions.Edit; - if (commit.op === SecretOperations.Delete) action = ProjectPermissionSecretActions.Delete; + let action = ProjectPermissionActions.Create; + if (commit.op === SecretOperations.Update) action = ProjectPermissionActions.Edit; + if (commit.op === SecretOperations.Delete) action = ProjectPermissionActions.Delete; ForbiddenError.from(permission).throwUnlessCan( action, diff --git a/backend/src/ee/services/secret-replication/secret-replication-service.ts b/backend/src/ee/services/secret-replication/secret-replication-service.ts index 5fae2675d0..3c25db98c4 100644 --- a/backend/src/ee/services/secret-replication/secret-replication-service.ts +++ b/backend/src/ee/services/secret-replication/secret-replication-service.ts @@ -265,7 +265,6 @@ export const secretReplicationServiceFactory = ({ folderDAL, secretImportDAL, decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""), - viewSecretValue: true, hasSecretAccess: () => true }); // secrets that gets replicated across imports diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts index 8d458111ab..02da4b7eaf 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts @@ -15,11 +15,7 @@ import { TSecretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret import { TLicenseServiceFactory } from "../license/license-service"; import { TPermissionServiceFactory } from "../permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "../permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { TSecretRotationDALFactory } from "./secret-rotation-dal"; import { TSecretRotationQueueFactory } from "./secret-rotation-queue"; import { TSecretRotationEncData } from "./secret-rotation-queue/secret-rotation-queue-types"; @@ -110,7 +106,7 @@ export const secretRotationServiceFactory = ({ }); } ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); diff --git a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts index e8eab30172..1c34f6b3d4 100644 --- a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts +++ b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts @@ -22,11 +22,7 @@ import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/se import { TLicenseServiceFactory } from "../license/license-service"; import { TPermissionServiceFactory } from "../permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "../permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { TGetSnapshotDataDTO, TProjectSnapshotCountDTO, @@ -38,7 +34,6 @@ import { TSnapshotFolderDALFactory } from "./snapshot-folder-dal"; import { TSnapshotSecretDALFactory } from "./snapshot-secret-dal"; import { TSnapshotSecretV2DALFactory } from "./snapshot-secret-v2-dal"; import { getFullFolderPath } from "./snapshot-service-fns"; -import { INFISICAL_SECRET_VALUE_HIDDEN_MASK } from "@app/services/secret/secret-fns"; type TSecretSnapshotServiceFactoryDep = { snapshotDAL: TSnapshotDALFactory; @@ -102,7 +97,7 @@ export const secretSnapshotServiceFactory = ({ // We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder. ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -139,7 +134,7 @@ export const secretSnapshotServiceFactory = ({ // We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder. ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -166,7 +161,6 @@ export const secretSnapshotServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - const shouldUseBridge = snapshot.projectVersion === 3; let snapshotDetails; if (shouldUseBridge) { @@ -175,110 +169,68 @@ export const secretSnapshotServiceFactory = ({ projectId: snapshot.projectId }); const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotV2DataById(id); - - const fullFolderPath = await getFullFolderPath({ - folderDAL, - folderId: encryptedSnapshotDetails.folderId, - envId: encryptedSnapshotDetails.environment.id - }); - snapshotDetails = { ...encryptedSnapshotDetails, - secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => { - const canReadValue = permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: encryptedSnapshotDetails.environment.slug, - secretPath: fullFolderPath, - secretName: el.key, - secretTags: el.tags.length ? el.tags.map((tag) => tag.slug) : undefined - }) - ); - - let secretValue = ""; - if (canReadValue) { - secretValue = el.encryptedValue - ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() - : ""; - } else { - secretValue = INFISICAL_SECRET_VALUE_HIDDEN_MASK; - } - - return { - ...el, - secretKey: el.key, - secretValueHidden: !canReadValue, - secretValue, - secretComment: el.encryptedComment - ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() - : "" - }; - }) + secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => ({ + ...el, + secretKey: el.key, + secretValue: el.encryptedValue + ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() + : "", + secretComment: el.encryptedComment + ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() + : "" + })) }; } else { const encryptedSnapshotDetails = await snapshotDAL.findSecretSnapshotDataById(id); - - const fullFolderPath = await getFullFolderPath({ - folderDAL, - folderId: encryptedSnapshotDetails.folderId, - envId: encryptedSnapshotDetails.environment.id - }); - const { botKey } = await projectBotService.getBotKey(snapshot.projectId); if (!botKey) throw new NotFoundError({ message: `Project bot key not found for project with ID '${snapshot.projectId}'` }); snapshotDetails = { ...encryptedSnapshotDetails, - secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ + secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => ({ + ...el, + secretKey: decryptSymmetric128BitHexKeyUTF8({ ciphertext: el.secretKeyCiphertext, iv: el.secretKeyIV, tag: el.secretKeyTag, key: botKey - }); - - const canReadValue = permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: encryptedSnapshotDetails.environment.slug, - secretPath: fullFolderPath, - secretName: secretKey, - secretTags: el.tags.length ? el.tags.map((tag) => tag.slug) : undefined - }) - ); - - let secretValue = ""; - - if (canReadValue) { - secretValue = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: el.secretValueCiphertext, - iv: el.secretValueIV, - tag: el.secretValueTag, - key: botKey - }); - } else { - secretValue = INFISICAL_SECRET_VALUE_HIDDEN_MASK; - } - - return { - ...el, - secretKey, - secretValueHidden: !canReadValue, - secretValue, - secretComment: - el.secretCommentTag && el.secretCommentIV && el.secretCommentCiphertext - ? decryptSymmetric128BitHexKeyUTF8({ - ciphertext: el.secretCommentCiphertext, - iv: el.secretCommentIV, - tag: el.secretCommentTag, - key: botKey - }) - : "" - }; - }) + }), + secretValue: decryptSymmetric128BitHexKeyUTF8({ + ciphertext: el.secretValueCiphertext, + iv: el.secretValueIV, + tag: el.secretValueTag, + key: botKey + }), + secretComment: + el.secretCommentTag && el.secretCommentIV && el.secretCommentCiphertext + ? decryptSymmetric128BitHexKeyUTF8({ + ciphertext: el.secretCommentCiphertext, + iv: el.secretCommentIV, + tag: el.secretCommentTag, + key: botKey + }) + : "" + })) }; } + const fullFolderPath = await getFullFolderPath({ + folderDAL, + folderId: snapshotDetails.folderId, + envId: snapshotDetails.environment.id + }); + + // We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder. + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Read, + subject(ProjectPermissionSub.Secrets, { + environment: snapshotDetails.environment.slug, + secretPath: fullFolderPath + }) + ); + return snapshotDetails; }; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 5a412cb45a..1b458175d1 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -666,7 +666,6 @@ export const SECRETS = { secretPath: "The path of the secret to attach tags to.", type: "The type of the secret to attach tags to. (shared/personal)", environment: "The slug of the environment where the secret is located", - viewSecretValue: "Whether or not to retrieve the secret value.", projectSlug: "The slug of the project where the secret is located.", tagSlugs: "An array of existing tag slugs to attach to the secret." }, @@ -690,7 +689,6 @@ export const RAW_SECRETS = { "The slug of the project to list secrets from. This parameter is only applicable by machine identities.", environment: "The slug of the environment to list secrets from.", secretPath: "The secret path to list secrets from.", - viewSecretValue: "Whether or not to retrieve the secret value.", includeImports: "Weather to include imported secrets or not.", tagSlugs: "The comma separated tag slugs to filter secrets.", metadataFilter: @@ -719,7 +717,6 @@ export const RAW_SECRETS = { secretPath: "The path of the secret to get.", version: "The version of the secret to get.", type: "The type of the secret to get.", - viewSecretValue: "Whether or not to retrieve the secret value.", includeImports: "Weather to include imported secrets or not." }, UPDATE: { diff --git a/backend/src/lib/errors/index.ts b/backend/src/lib/errors/index.ts index 7dbffed20f..cc1c1d66b4 100644 --- a/backend/src/lib/errors/index.ts +++ b/backend/src/lib/errors/index.ts @@ -1,5 +1,4 @@ /* eslint-disable max-classes-per-file */ - export class DatabaseError extends Error { name: string; diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index 024e442241..4d645ac4be 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -7,7 +7,6 @@ import { ProjectRolesSchema, ProjectsSchema, SecretApprovalPoliciesSchema, - SecretTagsSchema, UsersSchema } from "@app/db/schemas"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -233,11 +232,3 @@ export const SanitizedProjectSchema = ProjectsSchema.pick({ kmsCertificateKeyId: true, auditLogsRetentionDays: true }); - -export const SanitizedTagSchema = SecretTagsSchema.pick({ - id: true, - slug: true, - color: true -}).extend({ - name: z.string() -}); diff --git a/backend/src/server/routes/v1/dashboard-router.ts b/backend/src/server/routes/v1/dashboard-router.ts index 27ba3f8a35..d96c45f042 100644 --- a/backend/src/server/routes/v1/dashboard-router.ts +++ b/backend/src/server/routes/v1/dashboard-router.ts @@ -1,11 +1,10 @@ import { ForbiddenError, subject } from "@casl/ability"; import { z } from "zod"; -import { ActionProjectType, SecretFoldersSchema, SecretImportsSchema } from "@app/db/schemas"; +import { ActionProjectType, SecretFoldersSchema, SecretImportsSchema, SecretTagsSchema } from "@app/db/schemas"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; import { ProjectPermissionDynamicSecretActions, - ProjectPermissionSecretActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { DASHBOARD } from "@app/lib/api-docs"; @@ -16,7 +15,7 @@ import { secretsLimit } from "@app/server/config/rateLimiter"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { getUserAgentType } from "@app/server/plugins/audit-log"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { SanitizedDynamicSecretSchema, SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas"; +import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema"; import { SecretsOrderBy } from "@app/services/secret/secret-types"; @@ -117,10 +116,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { dynamicSecrets: SanitizedDynamicSecretSchema.extend({ environment: z.string() }).array().optional(), secrets: secretRawSchema .extend({ - secretValueHidden: z.boolean(), secretPath: z.string().optional(), secretMetadata: ResourceMetadataSchema.optional(), - tags: SanitizedTagSchema.array().optional() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional() }) .array() .optional(), @@ -289,7 +294,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { if (remainingLimit > 0 && totalSecretCount > adjustedOffset) { secrets = await server.services.secret.getSecretsRawMultiEnv({ - viewSecretValue: true, actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, @@ -389,7 +393,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { .optional(), search: z.string().trim().describe(DASHBOARD.SECRET_DETAILS_LIST.search).optional(), tags: z.string().trim().transform(decodeURIComponent).describe(DASHBOARD.SECRET_DETAILS_LIST.tags).optional(), - viewSecretValue: booleanSchema.default(true), includeSecrets: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeSecrets), includeFolders: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeFolders), includeDynamicSecrets: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeDynamicSecrets), @@ -407,10 +410,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { dynamicSecrets: SanitizedDynamicSecretSchema.array().optional(), secrets: secretRawSchema .extend({ - secretValueHidden: z.boolean(), secretPath: z.string().optional(), secretMetadata: ResourceMetadataSchema.optional(), - tags: SanitizedTagSchema.array().optional() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional() }) .array() .optional(), @@ -592,25 +601,23 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { }); if (remainingLimit > 0 && totalSecretCount > adjustedOffset) { - secrets = ( - await server.services.secret.getSecretsRaw({ - actorId: req.permission.id, - actor: req.permission.type, - viewSecretValue: req.query.viewSecretValue, - throwOnMissingReadValuePermission: false, - actorOrgId: req.permission.orgId, - environment, - actorAuthMethod: req.permission.authMethod, - projectId, - path: secretPath, - orderBy, - orderDirection, - search, - limit: remainingLimit, - offset: adjustedOffset, - tagSlugs: tags - }) - ).secrets; + const secretsRaw = await server.services.secret.getSecretsRaw({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + environment, + actorAuthMethod: req.permission.authMethod, + projectId, + path: secretPath, + orderBy, + orderDirection, + search, + limit: remainingLimit, + offset: adjustedOffset, + tagSlugs: tags + }); + + secrets = secretsRaw.secrets; await server.services.auditLog.createAuditLog({ projectId, @@ -689,10 +696,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { .optional(), secrets: secretRawSchema .extend({ - secretValueHidden: z.boolean(), secretPath: z.string().optional(), secretMetadata: ResourceMetadataSchema.optional(), - tags: SanitizedTagSchema.array().optional() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional() }) .array() .optional() @@ -736,7 +749,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { const secrets = await server.services.secret.getSecretsRawByFolderMappings( { - filterByAction: ProjectPermissionSecretActions.DescribeSecret, projectId, folderMappings, filters: { @@ -850,17 +862,22 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { projectId: z.string().trim(), environment: z.string().trim(), secretPath: z.string().trim().default("/").transform(removeTrailingSlash), - keys: z.string().trim().transform(decodeURIComponent), - viewSecretValue: booleanSchema.default(false) + keys: z.string().trim().transform(decodeURIComponent) }), response: { 200: z.object({ secrets: secretRawSchema .extend({ - secretValueHidden: z.boolean(), secretPath: z.string().optional(), secretMetadata: ResourceMetadataSchema.optional(), - tags: SanitizedTagSchema.array().optional() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional() }) .array() .optional() @@ -869,7 +886,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { secretPath, projectId, environment, viewSecretValue } = req.query; + const { secretPath, projectId, environment } = req.query; const keys = req.query.keys?.split(",").filter((key) => Boolean(key.trim())) ?? []; if (!keys.length) throw new BadRequestError({ message: "One or more keys required" }); @@ -878,7 +895,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, - viewSecretValue, environment, actorAuthMethod: req.permission.authMethod, projectId, diff --git a/backend/src/server/routes/v2/service-token-router.ts b/backend/src/server/routes/v2/service-token-router.ts index 1a84c9123a..fb10f17db6 100644 --- a/backend/src/server/routes/v2/service-token-router.ts +++ b/backend/src/server/routes/v2/service-token-router.ts @@ -94,7 +94,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => iv: z.string().trim(), tag: z.string().trim(), expiresIn: z.number().nullable(), - permissions: z.enum(["read", "write", "readValue"]).array() + permissions: z.enum(["read", "write"]).array() }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index 3bc312c981..a5dc39485e 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -1,7 +1,13 @@ import picomatch from "picomatch"; import { z } from "zod"; -import { SecretApprovalRequestsSchema, SecretsSchema, SecretType, ServiceTokenScopes } from "@app/db/schemas"; +import { + SecretApprovalRequestsSchema, + SecretsSchema, + SecretTagsSchema, + SecretType, + ServiceTokenScopes +} from "@app/db/schemas"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; import { RAW_SECRETS, SECRETS } from "@app/lib/api-docs"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -17,7 +23,7 @@ import { SecretOperations, SecretProtectionType } from "@app/services/secret/sec import { SecretUpdateMode } from "@app/services/secret-v2-bridge/secret-v2-bridge-types"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; -import { SanitizedTagSchema, secretRawSchema } from "../sanitizedSchemas"; +import { secretRawSchema } from "../sanitizedSchemas"; const SecretReferenceNode = z.object({ key: z.string(), @@ -25,14 +31,6 @@ const SecretReferenceNode = z.object({ environment: z.string(), secretPath: z.string() }); - -const convertStringBoolean = (defaultValue: boolean = false) => { - return z - .enum(["true", "false"]) - .default(defaultValue ? "true" : "false") - .transform((value) => value === "true"); -}; - type TSecretReferenceNode = z.infer & { children: TSecretReferenceNode[] }; const SecretReferenceNodeTree: z.ZodType = SecretReferenceNode.extend({ @@ -77,9 +75,17 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }).extend({ - tags: SanitizedTagSchema.array() - }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + }) + ) }) } }, @@ -133,7 +139,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ secret: SecretsSchema.omit({ secretBlindIndex: true }).extend({ - tags: SanitizedTagSchema.array() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() }) }) } @@ -235,10 +247,21 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug), environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment), secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath), - viewSecretValue: convertStringBoolean(true).describe(RAW_SECRETS.LIST.viewSecretValue), - expandSecretReferences: convertStringBoolean().describe(RAW_SECRETS.LIST.expand), - recursive: convertStringBoolean().describe(RAW_SECRETS.LIST.recursive), - include_imports: convertStringBoolean().describe(RAW_SECRETS.LIST.includeImports), + expandSecretReferences: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.LIST.expand), + recursive: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.LIST.recursive), + include_imports: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.LIST.includeImports), tagSlugs: z .string() .describe(RAW_SECRETS.LIST.tagSlugs) @@ -251,9 +274,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secrets: secretRawSchema .extend({ secretPath: z.string().optional(), - secretValueHidden: z.boolean(), secretMetadata: ResourceMetadataSchema.optional(), - tags: SanitizedTagSchema.array().optional() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional() }) .array(), imports: z @@ -264,7 +293,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secrets: secretRawSchema .omit({ createdAt: true, updatedAt: true }) .extend({ - secretValueHidden: z.boolean(), secretMetadata: ResourceMetadataSchema.optional() }) .array() @@ -314,7 +342,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { expandSecretReferences: req.query.expandSecretReferences, actorAuthMethod: req.permission.authMethod, projectId: workspaceId, - viewSecretValue: req.query.viewSecretValue, path: secretPath, metadataFilter: req.query.metadataFilter, includeImports: req.query.include_imports, @@ -349,7 +376,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } }); } - return { secrets, imports }; } }); @@ -377,15 +403,28 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath), version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version), type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.GET.type), - viewSecretValue: convertStringBoolean(true).describe(RAW_SECRETS.GET.viewSecretValue), - expandSecretReferences: convertStringBoolean().describe(RAW_SECRETS.GET.expand), - include_imports: convertStringBoolean().describe(RAW_SECRETS.GET.includeImports) + expandSecretReferences: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.GET.expand), + include_imports: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") + .describe(RAW_SECRETS.GET.includeImports) }), response: { 200: z.object({ secret: secretRawSchema.extend({ - secretValueHidden: z.boolean(), - tags: SanitizedTagSchema.array().optional(), + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() + .optional(), secretMetadata: ResourceMetadataSchema.optional() }) }) @@ -417,7 +456,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { expandSecretReferences: req.query.expandSecretReferences, environment, projectId: workspaceId, - viewSecretValue: req.query.viewSecretValue, projectSlug: workspaceSlug, path: secretPath, secretName: req.params.secretName, @@ -624,9 +662,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: secretRawSchema.extend({ - secretValueHidden: z.boolean() - }) + secret: secretRawSchema }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -722,9 +758,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: secretRawSchema.extend({ - secretValueHidden: z.boolean() - }) + secret: secretRawSchema }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -746,7 +780,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { if (secretOperation.type === SecretProtectionType.Approval) { return { approval: secretOperation.approval }; } - const { secret } = secretOperation; await server.services.auditLog.createAuditLog({ @@ -809,7 +842,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspace: z.string(), environment: z.string(), secretPath: z.string().optional(), - tags: SanitizedTagSchema.array() + tags: SecretTagsSchema.pick({ + id: true, + slug: true, + color: true + }) + .extend({ name: z.string() }) + .array() }) .array(), imports: z @@ -905,7 +944,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretPath: z.string().trim().default("/").transform(removeTrailingSlash), type: z.nativeEnum(SecretType).default(SecretType.Shared), version: z.coerce.number().optional(), - include_imports: convertStringBoolean() + include_imports: z + .enum(["true", "false"]) + .default("false") + .transform((value) => value === "true") }), response: { 200: z.object({ @@ -1176,7 +1218,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( z.object({ - secretValueHidden: z.boolean(), _id: z.string(), workspace: z.string(), environment: z.string() @@ -1346,12 +1387,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }).extend({ - _id: z.string(), - secretValueHidden: z.boolean(), - workspace: z.string(), - environment: z.string() - }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + _id: z.string(), + workspace: z.string(), + environment: z.string() + }) + ) }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -1663,7 +1705,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secrets: SecretsSchema.omit({ secretBlindIndex: true }).extend({ secretValueHidden: z.boolean() }).array() + secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -1778,11 +1820,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secrets: SecretsSchema.omit({ secretBlindIndex: true }) - .extend({ - secretValueHidden: z.boolean() - }) - .array() + secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -2044,7 +2082,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secrets: secretRawSchema.extend({ secretValueHidden: z.boolean() }).array() + secrets: secretRawSchema.array() }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) @@ -2166,11 +2204,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secrets: secretRawSchema - .extend({ - secretValueHidden: z.boolean() - }) - .array() + secrets: secretRawSchema.array() }), z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) diff --git a/backend/src/services/external-migration/external-migration-fns.ts b/backend/src/services/external-migration/external-migration-fns.ts index fe0d49ca8f..7446787923 100644 --- a/backend/src/services/external-migration/external-migration-fns.ts +++ b/backend/src/services/external-migration/external-migration-fns.ts @@ -31,9 +31,9 @@ export type TImportDataIntoInfisicalDTO = { projectEnvDAL: Pick; kmsService: Pick; - secretDAL: Pick; + secretDAL: Pick; secretVersionDAL: Pick; - secretTagDAL: Pick; + secretTagDAL: Pick; secretVersionTagDAL: Pick; resourceMetadataDAL: Pick; diff --git a/backend/src/services/external-migration/external-migration-queue.ts b/backend/src/services/external-migration/external-migration-queue.ts index 8aa46b94c1..e3d77e8329 100644 --- a/backend/src/services/external-migration/external-migration-queue.ts +++ b/backend/src/services/external-migration/external-migration-queue.ts @@ -27,9 +27,9 @@ export type TExternalMigrationQueueFactoryDep = { projectEnvDAL: Pick; kmsService: Pick; - secretDAL: Pick; + secretDAL: Pick; secretVersionDAL: Pick; - secretTagDAL: Pick; + secretTagDAL: Pick; secretVersionTagDAL: Pick; folderDAL: Pick; diff --git a/backend/src/services/integration-auth/integration-delete-secret.ts b/backend/src/services/integration-auth/integration-delete-secret.ts index 4b07245ac5..fdefd0e621 100644 --- a/backend/src/services/integration-auth/integration-delete-secret.ts +++ b/backend/src/services/integration-auth/integration-delete-secret.ts @@ -68,8 +68,7 @@ const getIntegrationSecretsV2 = async ( secretDAL: secretV2BridgeDAL, secretImportDAL, secretImports, - hasSecretAccess: () => true, - viewSecretValue: true + hasSecretAccess: () => true }); for (let i = importedSecrets.length - 1; i >= 0; i -= 1) { diff --git a/backend/src/services/integration/integration-service.ts b/backend/src/services/integration/integration-service.ts index f72357964d..35994433a0 100644 --- a/backend/src/services/integration/integration-service.ts +++ b/backend/src/services/integration/integration-service.ts @@ -2,11 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { NotFoundError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -96,7 +92,7 @@ export const integrationServiceFactory = ({ ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: sourceEnvironment, secretPath @@ -179,7 +175,7 @@ export const integrationServiceFactory = ({ if (environment || secretPath) { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: newEnvironment, secretPath: newSecretPath diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index d3afaec29e..e1653d3713 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -11,11 +11,7 @@ import { import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service"; import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types"; import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; @@ -751,10 +747,7 @@ export const projectServiceFactory = ({ actorOrgId, actionProjectType: ActionProjectType.Any }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSub.Secrets - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); const project = await projectDAL.findProjectById(projectId); diff --git a/backend/src/services/secret-import/secret-import-fns.ts b/backend/src/services/secret-import/secret-import-fns.ts index e5a4504412..d21ad3b9c3 100644 --- a/backend/src/services/secret-import/secret-import-fns.ts +++ b/backend/src/services/secret-import/secret-import-fns.ts @@ -3,7 +3,6 @@ import { groupBy, unique } from "@app/lib/fn"; import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema"; import { TSecretDALFactory } from "../secret/secret-dal"; -import { INFISICAL_SECRET_VALUE_HIDDEN_MASK } from "../secret/secret-fns"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal"; import { TSecretImportDALFactory } from "./secret-import-dal"; @@ -33,12 +32,6 @@ type TSecretImportSecretsV2 = { folderId: string | undefined; importFolderId: string; secrets: (TSecretsV2 & { - secretTags: { - slug: string; - name: string; - color?: string | null; - id: string; - }[]; workspace: string; environment: string; _id: string; @@ -46,7 +39,6 @@ type TSecretImportSecretsV2 = { // akhilmhdh: yes i know you can put ?. // But for somereason ts consider ? and undefined explicit as different just ts things secretValue: string; - secretValueHidden: boolean; secretComment: string; secretMetadata?: ResourceMetadataDTO; })[]; @@ -158,14 +150,12 @@ export const fnSecretsV2FromImports = async ({ secretImportDAL, decryptor, expandSecretReferences, - hasSecretAccess, - viewSecretValue + hasSecretAccess }: { secretImports: (Omit & { importEnv: { id: string; slug: string; name: string }; })[]; folderDAL: Pick; - viewSecretValue: boolean; secretDAL: Pick; secretImportDAL: Pick; decryptor: (value?: Buffer | null) => string; @@ -178,14 +168,9 @@ export const fnSecretsV2FromImports = async ({ hasSecretAccess: (environment: string, secretPath: string, secretName: string, secretTagSlugs: string[]) => boolean; }) => { const cyclicDetector = new Set(); - const stack: { - secretImports: typeof rootSecretImports; - depth: number; - parentImportedSecrets: (TSecretsV2 & { - secretValueHidden: boolean; - secretTags: { slug: string; name: string; id: string; color?: string | null }[]; - })[]; - }[] = [{ secretImports: rootSecretImports, depth: 0, parentImportedSecrets: [] }]; + const stack: { secretImports: typeof rootSecretImports; depth: number; parentImportedSecrets: TSecretsV2[] }[] = [ + { secretImports: rootSecretImports, depth: 0, parentImportedSecrets: [] } + ]; const processedImports: TSecretImportSecretsV2[] = []; @@ -244,9 +229,7 @@ export const fnSecretsV2FromImports = async ({ .map((item) => ({ ...item, secretKey: item.key, - secretValue: viewSecretValue ? decryptor(item.encryptedValue) : INFISICAL_SECRET_VALUE_HIDDEN_MASK, - secretValueHidden: !viewSecretValue, - secretTags: item.tags, + secretValue: decryptor(item.encryptedValue), secretComment: decryptor(item.encryptedComment), environment: importEnv.slug, workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend. @@ -284,8 +267,6 @@ export const fnSecretsV2FromImports = async ({ processedImport.secrets = unique(processedImport.secrets, (i) => i.key); return Promise.allSettled( processedImport.secrets.map(async (decryptedSecret, index) => { - if (decryptedSecret.secretValueHidden) return; - const expandedSecretValue = await expandSecretReferences({ value: decryptedSecret.secretValue, secretPath: processedImport.secretPath, diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index 6c412edc07..e8fde04d12 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -5,11 +5,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { ActionProjectType, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getReplicationFolderName } from "@app/ee/services/secret-replication/secret-replication-service"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -94,7 +90,7 @@ export const secretImportServiceFactory = ({ // check if user has permission to import from target path ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: data.environment, secretPath: data.path @@ -406,7 +402,7 @@ export const secretImportServiceFactory = ({ // check if user has permission to import from target path ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: secretImportDoc.importEnv.slug, secretPath: secretImportDoc.importPath @@ -600,7 +596,7 @@ export const secretImportServiceFactory = ({ const secretImports = await secretImportDAL.find({ folderId: folder.id, isReplication: false }); const allowedImports = secretImports.filter((el) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: el.importEnv.slug, secretPath: el.importPath @@ -646,13 +642,12 @@ export const secretImportServiceFactory = ({ const importedSecrets = await fnSecretsV2FromImports({ secretImports, folderDAL, - viewSecretValue: true, secretDAL: secretV2BridgeDAL, secretImportDAL, decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""), hasSecretAccess: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, @@ -672,7 +667,7 @@ export const secretImportServiceFactory = ({ const allowedImports = secretImports.filter((el) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: el.importEnv.slug, secretPath: el.importPath @@ -688,10 +683,7 @@ export const secretImportServiceFactory = ({ return importedSecrets.map((el) => ({ ...el, secrets: el.secrets.map((encryptedSecret) => - decryptSecretRaw( - { ...encryptedSecret, workspace: projectId, environment, secretPath, secretValueHidden: false }, - botKey - ) + decryptSecretRaw({ ...encryptedSecret, workspace: projectId, environment, secretPath }, botKey) ) })); }; diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index 8afcf6416b..cdc9540da7 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -249,8 +249,7 @@ export const secretSyncQueueFactory = ({ expandSecretReferences, secretImportDAL, secretImports, - hasSecretAccess: () => true, - viewSecretValue: true + hasSecretAccess: () => true }); for (let i = importedSecrets.length - 1; i >= 0; i -= 1) { diff --git a/backend/src/services/secret-sync/secret-sync-service.ts b/backend/src/services/secret-sync/secret-sync-service.ts index 7b92a463c2..8211180e8b 100644 --- a/backend/src/services/secret-sync/secret-sync-service.ts +++ b/backend/src/services/secret-sync/secret-sync-service.ts @@ -3,7 +3,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { - ProjectPermissionSecretActions, + ProjectPermissionActions, ProjectPermissionSecretSyncActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -179,7 +179,7 @@ export const secretSyncServiceFactory = ({ ); ForbiddenError.from(projectPermission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath @@ -270,7 +270,7 @@ export const secretSyncServiceFactory = ({ throw new BadRequestError({ message: "Must specify both source environment and secret path" }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: updatedEnvironment, secretPath: updatedSecretPath diff --git a/backend/src/services/secret-tag/secret-tag-dal.ts b/backend/src/services/secret-tag/secret-tag-dal.ts index 3b9151557e..1df64afa20 100644 --- a/backend/src/services/secret-tag/secret-tag-dal.ts +++ b/backend/src/services/secret-tag/secret-tag-dal.ts @@ -47,7 +47,6 @@ export const secretTagDALFactory = (db: TDbClient) => { throw new DatabaseError({ error, name: "Find all by ids" }); } }; - return { ...secretTagOrm, saveTagsToSecret: secretJnTagOrm.insertMany, diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts index 6eb95e9b92..cc40b0f268 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts @@ -7,7 +7,6 @@ import { logger } from "@app/lib/logger"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema"; -import { INFISICAL_SECRET_VALUE_HIDDEN_MASK } from "../secret/secret-fns"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal"; import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types"; @@ -103,7 +102,6 @@ export const fnSecretBulkInsert = async ({ [`${TableName.SecretV2}Id` as const]: newSecretGroupedByKeyName[key][0].id })) ); - const secretVersions = await secretVersionDAL.insertMany( sanitizedInputSecrets.map((el) => ({ ...el, @@ -139,7 +137,6 @@ export const fnSecretBulkInsert = async ({ if (newSecretTags.length) { const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx); const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId); - const newSecretVersionTags = secTags.flatMap(({ secrets_v2Id, secret_tagsId }) => ({ [`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id, [`${TableName.SecretTag}Id` as const]: secret_tagsId @@ -148,16 +145,7 @@ export const fnSecretBulkInsert = async ({ await secretVersionTagDAL.insertMany(newSecretVersionTags, tx); } - const secretsWithTags = await secretDAL.find( - { - $in: { - [`${TableName.SecretV2}.id` as "id"]: newSecrets.map((s) => s.id) - } - }, - { tx } - ); - - return secretsWithTags.map((secret) => ({ ...secret, _id: secret.id })); + return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; export const fnSecretBulkUpdate = async ({ @@ -295,15 +283,7 @@ export const fnSecretBulkUpdate = async ({ tx ); - const secretsWithTags = await secretDAL.find( - { - $in: { - [`${TableName.SecretV2}.id` as "id"]: newSecrets.map((s) => s.id) - } - }, - { tx } - ); - return secretsWithTags.map((secret) => ({ ...secret, _id: secret.id })); + return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; export const fnSecretBulkDelete = async ({ @@ -536,7 +516,7 @@ export const expandSecretReferencesFactory = ({ const referredValue = await fetchSecret(environment, secretPath, secretKey); if (!canExpandValue(environment, secretPath, secretKey, referredValue.tags)) 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 read value on.` + 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); @@ -555,7 +535,7 @@ export const expandSecretReferencesFactory = ({ const referedValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey); if (!canExpandValue(secretReferenceEnvironment, secretReferencePath, secretReferenceKey, referedValue.tags)) throw new ForbiddenRequestError({ - message: `You are attempting to reference secret named ${secretReferenceKey} from environment ${secretReferenceEnvironment} in path ${secretReferencePath} which you do not have access to read value on.` + message: `You are attempting to reference secret named ${secretReferenceKey} from environment ${secretReferenceEnvironment} in path ${secretReferencePath} which you do not have access to.` }); const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath); @@ -643,13 +623,13 @@ export const reshapeBridgeSecret = ( name: string; }[]; secretMetadata?: ResourceMetadataDTO; - }, - secretValueHidden: boolean + } ) => ({ secretKey: secret.key, secretPath, workspace: workspaceId, environment, + secretValue: secret.value || "", secretComment: secret.comment || "", version: secret.version, type: secret.type, @@ -663,15 +643,5 @@ export const reshapeBridgeSecret = ( metadata: secret.metadata, secretMetadata: secret.secretMetadata, createdAt: secret.createdAt, - updatedAt: secret.updatedAt, - - ...(secretValueHidden - ? { - secretValue: INFISICAL_SECRET_VALUE_HIDDEN_MASK, - secretValueHidden: true - } - : { - secretValue: secret.value || "", - secretValueHidden: false - }) + updatedAt: secret.updatedAt }); diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts index 7f69ae7f1e..0ffb0ea4cd 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts @@ -11,11 +11,7 @@ import { TSecretsV2 } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal"; import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal"; @@ -77,13 +73,7 @@ type TSecretV2BridgeServiceFactoryDep = { projectEnvDAL: Pick; folderDAL: Pick< TSecretFolderDALFactory, - | "findBySecretPath" - | "updateById" - | "findById" - | "findByManySecretPath" - | "find" - | "findBySecretPathMultiEnv" - | "findSecretPathByFolderIds" + "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" | "findBySecretPathMultiEnv" >; secretImportDAL: Pick; secretQueueService: Pick; @@ -262,7 +252,7 @@ export const secretV2BridgeServiceFactory = ({ const { secretName, type, ...inputSecretData } = inputSecret; ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -281,8 +271,8 @@ export const secretV2BridgeServiceFactory = ({ type: KmsDataKey.SecretManager, projectId }); - const secret = await secretDAL.transaction(async (tx) => { - const [createdSecret] = await fnSecretBulkInsert({ + const secret = await secretDAL.transaction((tx) => + fnSecretBulkInsert({ folderId, orgId: actorOrgId, inputSecrets: [ @@ -312,10 +302,8 @@ export const secretV2BridgeServiceFactory = ({ secretTagDAL, secretVersionTagDAL, tx - }); - - return createdSecret; - }); + }) + ); if (inputSecret.type === SecretType.Shared) { await snapshotService.performSnapshot(folderId); @@ -329,17 +317,11 @@ export const secretV2BridgeServiceFactory = ({ }); } - return reshapeBridgeSecret( - projectId, - environment, - secretPath, - { - ...secret, - value: inputSecret.secretValue, - comment: inputSecret.secretComment || "" - }, - false - ); + return reshapeBridgeSecret(projectId, environment, secretPath, { + ...secret[0], + value: inputSecret.secretValue, + comment: inputSecret.secretComment || "" + }); }; const updateSecret = async ({ @@ -408,7 +390,7 @@ export const secretV2BridgeServiceFactory = ({ } ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -419,22 +401,18 @@ export const secretV2BridgeServiceFactory = ({ // validate tags // fetch all tags and if not same count throw error meaning one was invalid tags - const newTags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : []; - if ((inputSecret.tagIds || []).length !== newTags.length) - throw new NotFoundError({ message: `Tag not found. Found ${newTags.map((el) => el.slug).join(",")}` }); - - const tagsToCheck = inputSecret.tagIds ? newTags : secret.tags; + 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. Found ${tags.map((el) => el.slug).join(",")}` }); // now check with new ids ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, secretName: inputSecret.secretName, - ...(tagsToCheck.length && { - secretTags: tagsToCheck.map((el) => el.slug) - }) + secretTags: tags?.map((el) => el.slug) }) ); @@ -446,14 +424,12 @@ export const secretV2BridgeServiceFactory = ({ }); if (doesNewNameSecretExist) throw new BadRequestError({ message: "Secret with the new name already exist" }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, secretName: inputSecret.newSecretName, - ...(tagsToCheck.length && { - secretTags: tagsToCheck.map((el) => el.slug) - }) + secretTags: tags?.map((el) => el.slug) }) ); } @@ -531,29 +507,11 @@ export const secretV2BridgeServiceFactory = ({ }); } - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: inputSecret.secretName, - ...(tagsToCheck.length && { - secretTags: tagsToCheck.map((el) => el.slug) - }) - }) - ); - - return reshapeBridgeSecret( - projectId, - environment, - secretPath, - { - ...updatedSecret[0], - value: inputSecret.secretValue || "", - comment: inputSecret.secretComment || "" - }, - secretValueHidden - ); + return reshapeBridgeSecret(projectId, environment, secretPath, { + ...updatedSecret[0], + value: inputSecret.secretValue || "", + comment: inputSecret.secretComment || "" + }); }; const deleteSecret = async ({ @@ -599,7 +557,7 @@ export const secretV2BridgeServiceFactory = ({ }); if (!secretToDelete) throw new NotFoundError({ message: "Secret not found" }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -641,32 +599,15 @@ export const secretV2BridgeServiceFactory = ({ type: KmsDataKey.SecretManager, projectId }); - - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: secretToDelete.key, - secretTags: secretToDelete.tags?.map((el) => el.slug) - }) - ); - - return reshapeBridgeSecret( - projectId, - environment, - secretPath, - { - ...deletedSecret[0], - value: deletedSecret[0].encryptedValue - ? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedValue }).toString() - : "", - comment: deletedSecret[0].encryptedComment - ? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedComment }).toString() - : "" - }, - secretValueHidden - ); + return reshapeBridgeSecret(projectId, environment, secretPath, { + ...deletedSecret[0], + value: deletedSecret[0].encryptedValue + ? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedValue }).toString() + : "", + comment: deletedSecret[0].encryptedComment + ? secretManagerDecryptor({ cipherTextBlob: deletedSecret[0].encryptedComment }).toString() + : "" + }); }; // get unique secrets count for multiple envs @@ -694,10 +635,7 @@ export const secretV2BridgeServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSub.Secrets - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); } const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, path); @@ -744,10 +682,7 @@ export const secretV2BridgeServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSub.Secrets - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) return 0; @@ -758,13 +693,7 @@ export const secretV2BridgeServiceFactory = ({ }; const getSecretsByFolderMappings = async ( - { - projectId, - userId, - filters, - folderMappings, - filterByAction = ProjectPermissionSecretActions.ReadValue - }: TGetSecretsRawByFolderMappingsDTO, + { projectId, userId, filters, folderMappings }: TGetSecretsRawByFolderMappingsDTO, projectPermission: Awaited>["permission"] ) => { const groupedFolderMappings = groupBy(folderMappings, (folderMapping) => folderMapping.folderId); @@ -784,7 +713,7 @@ export const secretV2BridgeServiceFactory = ({ const decryptedSecrets = secrets .filter((el) => projectPermission.can( - filterByAction, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: groupedFolderMappings[el.folderId][0].environment, secretPath: groupedFolderMappings[el.folderId][0].path, @@ -793,19 +722,8 @@ export const secretV2BridgeServiceFactory = ({ }) ) ) - .map((secret) => { - // Note(Daniel): This is only relevant if the filterAction isn't set to ReadValue. This is needed for the frontend. - const secretValueHidden = !projectPermission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: groupedFolderMappings[secret.folderId][0].environment, - secretPath: groupedFolderMappings[secret.folderId][0].path, - secretName: secret.key, - secretTags: secret.tags.map((i) => i.slug) - }) - ); - - return reshapeBridgeSecret( + .map((secret) => + reshapeBridgeSecret( projectId, groupedFolderMappings[secret.folderId][0].environment, groupedFolderMappings[secret.folderId][0].path, @@ -817,10 +735,9 @@ export const secretV2BridgeServiceFactory = ({ comment: secret.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString() : "" - }, - secretValueHidden - ); - }); + } + ) + ); return decryptedSecrets; }; @@ -849,10 +766,7 @@ export const secretV2BridgeServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); if (!isInternal) { - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSub.Secrets - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); } const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, path); @@ -872,8 +786,7 @@ export const secretV2BridgeServiceFactory = ({ projectId, folderMappings, filters: params, - userId: actorId, - filterByAction: ProjectPermissionSecretActions.DescribeSecret + userId: actorId }, permission ); @@ -888,12 +801,10 @@ export const secretV2BridgeServiceFactory = ({ projectId, actor, actorOrgId, - viewSecretValue, actorAuthMethod, includeImports, recursive, expandSecretReferences: shouldExpandSecretReferences, - throwOnMissingReadValuePermission = true, ...params }: TGetSecretsDTO) => { const { permission } = await permissionService.getProjectPermission({ @@ -905,14 +816,7 @@ export const secretV2BridgeServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: path, - secretTags: params.tagSlugs - }) - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets); let paths: { folderId: string; path: string }[] = []; @@ -950,87 +854,28 @@ export const secretV2BridgeServiceFactory = ({ }); const decryptedSecrets = secrets - .filter((el) => { - const canDescribeSecret = permission.can( - ProjectPermissionSecretActions.DescribeSecret, + .filter((el) => + permission.can( + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: groupedPaths[el.folderId][0].path, secretName: el.key, secretTags: el.tags.map((i) => i.slug) }) - ); - - if (!canDescribeSecret) { - return false; - } - - if (viewSecretValue) { - // Recursive secret, should be filtered out - if (groupedPaths[el.folderId][0].path !== path) { - const canReadRecursiveSecretValue = permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: groupedPaths[el.folderId][0].path, - secretName: el.key, - secretTags: el.tags.map((i) => i.slug) - }) - ); - - if (!canReadRecursiveSecretValue) { - return false; - } - } - - if (throwOnMissingReadValuePermission) { - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: groupedPaths[el.folderId][0].path, - secretName: el.key, - secretTags: el.tags.map((i) => i.slug) - }) - ); - } - // Else, we do nothing. Because we don't want to filter out the secret, OR throw an error. - // If the user doesn't have access to read the value, in the below map function, we mask the secret value and return the secret with a hidden value. - } - - return canDescribeSecret; - }) - .map((secret) => { - const isPersonalSecret = secret.userId === actorId && secret.type === SecretType.Personal; - - const secretValueHidden = - !viewSecretValue || - !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: groupedPaths[secret.folderId][0].path, - secretName: secret.key, - secretTags: secret.tags.map((i) => i.slug) - }) - ); - - return reshapeBridgeSecret( - projectId, - environment, - groupedPaths[secret.folderId][0].path, - { - ...secret, - value: secret.encryptedValue - ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString() - : "", - comment: secret.encryptedComment - ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString() - : "" - }, - secretValueHidden && !isPersonalSecret - ); - }); + ) + ) + .map((secret) => + reshapeBridgeSecret(projectId, environment, groupedPaths[secret.folderId][0].path, { + ...secret, + value: secret.encryptedValue + ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString() + : "", + comment: secret.encryptedComment + ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString() + : "" + }) + ); const { expandSecretReferences } = expandSecretReferencesFactory({ projectId, @@ -1039,7 +884,7 @@ export const secretV2BridgeServiceFactory = ({ decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined), canExpandValue: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, @@ -1078,36 +923,22 @@ export const secretV2BridgeServiceFactory = ({ const secretImports = await secretImportDAL.findByFolderIds(paths.map((p) => p.folderId)); const allowedImports = secretImports.filter(({ isReplication }) => !isReplication); const importedSecrets = await fnSecretsV2FromImports({ - viewSecretValue, secretImports: allowedImports, secretDAL, folderDAL, secretImportDAL, expandSecretReferences, decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""), - hasSecretAccess: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => { - const canDescribe = permission.can( - ProjectPermissionSecretActions.DescribeSecret, + hasSecretAccess: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => + permission.can( + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, secretName: expandSecretKey, secretTags: expandSecretTags }) - ); - - const canReadValue = permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: expandEnvironment, - secretPath: expandSecretPath, - secretName: expandSecretKey, - secretTags: expandSecretTags - }) - ); - - return viewSecretValue ? canDescribe && canReadValue : canDescribe; - } + ) }); return { @@ -1127,7 +958,6 @@ export const secretV2BridgeServiceFactory = ({ type, secretName, version, - viewSecretValue, includeImports, expandSecretReferences: shouldExpandSecretReferences }: TGetASecretDTO) => { @@ -1189,7 +1019,7 @@ export const secretV2BridgeServiceFactory = ({ )); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path, @@ -1198,93 +1028,56 @@ export const secretV2BridgeServiceFactory = ({ }) ); - // this will throw if the user doesn't have read value permission no matter what - // because if its an expansion, it will fully depend on the value. const { expandSecretReferences } = expandSecretReferencesFactory({ projectId, folderDAL, secretDAL, decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined), - canExpandValue: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => { - return permission.can( - ProjectPermissionSecretActions.ReadValue, + canExpandValue: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => + permission.can( + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, secretName: expandSecretKey, secretTags: expandSecretTags }) - ); - } + ) }); // now if secret is not found // then search for imported secrets // here we consider the import order also thus starting from bottom - - // currently filters out the secrets that the user doesn't have access to read value on if (!secret && includeImports) { const secretImports = await secretImportDAL.find({ folderId, isReplication: false }); const importedSecrets = await fnSecretsV2FromImports({ secretImports, - viewSecretValue, secretDAL, folderDAL, secretImportDAL, decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""), expandSecretReferences: shouldExpandSecretReferences ? expandSecretReferences : undefined, - hasSecretAccess: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => { - return permission.can( - ProjectPermissionSecretActions.DescribeSecret, + hasSecretAccess: (expandEnvironment, expandSecretPath, expandSecretKey, expandSecretTags) => + permission.can( + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, secretName: expandSecretKey, secretTags: expandSecretTags }) - ); - } + ) }); for (let i = importedSecrets.length - 1; i >= 0; i -= 1) { for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) { const importedSecret = importedSecrets[i].secrets[j]; if (secretName === importedSecret.key) { - let secretValueHidden = true; - - if (viewSecretValue) { - if ( - !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: importedSecret.environment, - secretPath: importedSecrets[i].secretPath, - secretName: importedSecret.key, - secretTags: (importedSecret.secretTags || []).map((el) => el.slug) - }) - ) && - secretType !== SecretType.Personal - ) { - throw new ForbiddenRequestError({ - message: `You do not have permission to view secret import value on secret with name '${secretName}'`, - name: "ForbiddenReadSecretError" - }); - } - - secretValueHidden = false; - } - - return reshapeBridgeSecret( - projectId, - importedSecrets[i].environment, - importedSecrets[i].secretPath, - { - ...importedSecret, - value: importedSecret.secretValue || "", - comment: importedSecret.secretComment || "" - }, - secretValueHidden - ); + return reshapeBridgeSecret(projectId, importedSecrets[i].environment, importedSecrets[i].secretPath, { + ...importedSecret, + value: importedSecret.secretValue || "", + comment: importedSecret.secretComment || "" + }); } } } @@ -1306,43 +1099,13 @@ export const secretV2BridgeServiceFactory = ({ secretValue = expandedSecretValue || ""; } - let secretValueHidden = true; - - if (viewSecretValue) { - if ( - !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: path, - secretName, - secretTags: (secret?.tags || []).map((el) => el.slug) - }) - ) && - secretType !== SecretType.Personal - ) { - throw new ForbiddenRequestError({ - message: `You do not have permission to view secret value on secret with name '${secretName}'`, - name: "ForbiddenReadSecretError" - }); - } - - secretValueHidden = false; - } - - return reshapeBridgeSecret( - projectId, - environment, - path, - { - ...secret, - value: secretValue, - comment: secret.encryptedComment - ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString() - : "" - }, - secretValueHidden - ); + return reshapeBridgeSecret(projectId, environment, path, { + ...secret, + value: secretValue, + comment: secret.encryptedComment + ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString() + : "" + }); }; const createManySecret = async ({ @@ -1410,7 +1173,7 @@ export const secretV2BridgeServiceFactory = ({ inputSecrets.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1481,29 +1244,13 @@ export const secretV2BridgeServiceFactory = ({ environmentSlug: folder.environment.slug }); - return newSecrets.map((el) => { - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: el.key, - secretTags: el.tags?.map((i) => i.slug) - }) - ); - - return reshapeBridgeSecret( - projectId, - environment, - secretPath, - { - ...el, - value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", - comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" - }, - secretValueHidden - ); - }); + return newSecrets.map((el) => + reshapeBridgeSecret(projectId, environment, secretPath, { + ...el, + value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", + comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" + }) + ); }; const updateManySecret = async ({ @@ -1546,17 +1293,7 @@ export const secretV2BridgeServiceFactory = ({ const { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId }); - const updatedSecrets: Array< - TSecretsV2 & { - secretPath: string; - tags: { - id: string; - slug: string; - color?: string | null; - name: string; - }[]; - } - > = []; + const updatedSecrets: Array = []; await secretDAL.transaction(async (tx) => { for await (const folder of folders) { if (!folder) throw new NotFoundError({ message: "Folder not found" }); @@ -1607,7 +1344,7 @@ export const secretV2BridgeServiceFactory = ({ secretsToUpdateInDB.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1627,7 +1364,7 @@ export const secretV2BridgeServiceFactory = ({ if (updateMode === SecretUpdateMode.Upsert) { secretsToCreate.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1641,7 +1378,7 @@ export const secretV2BridgeServiceFactory = ({ // check again to avoid non authorized tags are removed secretsToUpdate.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1693,7 +1430,7 @@ export const secretV2BridgeServiceFactory = ({ secretsWithNewName.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1755,7 +1492,6 @@ export const secretV2BridgeServiceFactory = ({ secretVersionTagDAL, resourceMetadataDAL }); - updatedSecrets.push(...bulkUpdatedSecrets.map((el) => ({ ...el, secretPath: folder.path }))); if (updateMode === SecretUpdateMode.Upsert) { const bulkInsertedSecrets = await fnSecretBulkInsert({ @@ -1788,7 +1524,6 @@ export const secretV2BridgeServiceFactory = ({ secretVersionTagDAL, tx }); - updatedSecrets.push(...bulkInsertedSecrets.map((el) => ({ ...el, secretPath: folder.path }))); } } @@ -1810,33 +1545,13 @@ export const secretV2BridgeServiceFactory = ({ ) ); - return updatedSecrets.map((el) => { - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: el.secretPath, - secretName: el.key, - secretTags: el.tags.map((i) => i.slug) - }) - ); - - return { - ...reshapeBridgeSecret( - projectId, - environment, - el.secretPath, - { - ...el, - value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", - comment: el.encryptedComment - ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() - : "" - }, - secretValueHidden - ) - }; - }); + return updatedSecrets.map((el) => + reshapeBridgeSecret(projectId, environment, el.secretPath, { + ...el, + value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", + comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" + }) + ); }; const deleteManySecret = async ({ @@ -1898,7 +1613,7 @@ export const secretV2BridgeServiceFactory = ({ }); secretsToDelete.forEach((el) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -1937,35 +1652,13 @@ export const secretV2BridgeServiceFactory = ({ type: KmsDataKey.SecretManager, projectId }); - return secretsDeleted.map((el) => { - const secretToDeleteMatch = secretsToDelete.find( - (i) => i.key === el.key && (i.type || SecretType.Shared) === el.type - ); - - const secretValueHidden = - !secretToDeleteMatch || - !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: el.key, - secretTags: secretToDeleteMatch.tags?.map((i) => i.slug) - }) - ); - - return reshapeBridgeSecret( - projectId, - environment, - secretPath, - { - ...el, - value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", - comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" - }, - secretValueHidden - ); - }); + return secretsDeleted.map((el) => + reshapeBridgeSecret(projectId, environment, secretPath, { + ...el, + value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", + comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" + }) + ); }; const getSecretVersions = async ({ @@ -1978,18 +1671,11 @@ export const secretV2BridgeServiceFactory = ({ secretId }: TGetSecretVersionsDTO) => { const secret = await secretDAL.findById(secretId); - if (!secret) throw new NotFoundError({ message: `Secret with ID '${secretId}' not found` }); const folder = await folderDAL.findById(secret.folderId); if (!folder) throw new NotFoundError({ message: `Folder with ID '${secret.folderId}' not found` }); - const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(folder.projectId, [folder.id]); - - if (!folderWithPath) { - throw new NotFoundError({ message: `Folder with ID '${folder.id}' not found` }); - } - const { permission } = await permissionService.getProjectPermission({ actor, actorId, @@ -2003,36 +1689,14 @@ export const secretV2BridgeServiceFactory = ({ type: KmsDataKey.SecretManager, projectId: folder.projectId }); - - const secretVersions = await secretVersionDAL.findBySecretId(secretId, { - offset, - limit, - sort: [["createdAt", "desc"]] - }); - return secretVersions.map((el) => { - const secretValueHidden = permission.cannot( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: folder.environment.envSlug, - secretPath: folderWithPath.path, - secretName: el.key, - ...(el.tags?.length && { - secretTags: el.tags.map((tag) => tag.slug) - }) - }) - ); - return reshapeBridgeSecret( - folder.projectId, - folder.environment.envSlug, - folderWithPath.path, - { - ...el, - value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", - comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" - }, - secretValueHidden - ); - }); + const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] }); + return secretVersions.map((el) => + reshapeBridgeSecret(folder.projectId, folder.environment.envSlug, "/", { + ...el, + value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", + comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : "" + }) + ); }; // this is a backfilling API for secret references @@ -2127,26 +1791,16 @@ export const secretV2BridgeServiceFactory = ({ [`${TableName.SecretV2}.id` as "id"]: secretIds } }); - - const sourceActions = [ - ProjectPermissionSecretActions.Delete, - ProjectPermissionSecretActions.ReadValue, - ProjectPermissionSecretActions.DescribeSecret - ] as const; - const destinationActions = [ProjectPermissionSecretActions.Create, ProjectPermissionSecretActions.Edit] as const; - sourceSecrets.forEach((secret) => { - for (const sourceAction of sourceActions) { - ForbiddenError.from(permission).throwUnlessCan( - sourceAction, - subject(ProjectPermissionSub.Secrets, { - environment: sourceEnvironment, - secretPath: sourceSecretPath, - secretName: secret.key, - secretTags: secret.tags.map((el) => el.slug) - }) - ); - } + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Delete, + subject(ProjectPermissionSub.Secrets, { + environment: sourceEnvironment, + secretPath: sourceSecretPath, + secretName: secret.key, + secretTags: secret.tags.map((el) => el.slug) + }) + ); }); if (sourceSecrets.length !== secretIds.length) { @@ -2221,17 +1875,27 @@ export const secretV2BridgeServiceFactory = ({ // permission check whether can create or edit the ones in the destination folder locallyCreatedSecrets.forEach((secret) => { - for (const destinationAction of destinationActions) { - ForbiddenError.from(permission).throwUnlessCan( - destinationAction, - subject(ProjectPermissionSub.Secrets, { - environment: destinationEnvironment, - secretPath: destinationFolder.path, - secretName: secret.key, - secretTags: secret.tags.map((el) => el.slug) - }) - ); - } + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Create, + subject(ProjectPermissionSub.Secrets, { + environment: destinationEnvironment, + secretPath: destinationEnvironment, + secretName: secret.key, + secretTags: secret.tags.map((el) => el.slug) + }) + ); + }); + + locallyUpdatedSecrets.forEach((secret) => { + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Edit, + subject(ProjectPermissionSub.Secrets, { + environment: destinationEnvironment, + secretPath: destinationEnvironment, + secretName: secret.key, + secretTags: secret.tags.map((el) => el.slug) + }) + ); }); const destinationFolderPolicy = await secretApprovalPolicyService.getSecretApprovalPolicy( @@ -2461,7 +2125,7 @@ export const secretV2BridgeServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); @@ -2485,7 +2149,7 @@ export const secretV2BridgeServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -2494,7 +2158,7 @@ export const secretV2BridgeServiceFactory = ({ }) ); - const decryptedSecretValue = secret.encryptedValue + const secretValue = secret.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString() : ""; @@ -2505,7 +2169,7 @@ export const secretV2BridgeServiceFactory = ({ decryptSecretValue: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : undefined), canExpandValue: (expandEnvironment, expandSecretPath, expandSecretName, expandSecretTags) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: expandEnvironment, secretPath: expandSecretPath, @@ -2515,26 +2179,10 @@ export const secretV2BridgeServiceFactory = ({ ) }); - if ( - !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName, - secretTags: (secret?.tags || []).map((el) => el.slug) - }) - ) - ) { - throw new ForbiddenRequestError({ - message: `Unable to get secret reference tree for secret with key '${secretName}', because you don't have permission to view secret value.` - }); - } - const { expandedValue, stackTrace } = await getExpandedSecretStackTrace({ environment, secretPath, - value: decryptedSecretValue + value: secretValue }); return { tree: stackTrace, value: expandedValue }; diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts index 9cfe49b837..ad8264e810 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-types.ts @@ -1,7 +1,6 @@ import { Knex } from "knex"; import { SecretType, TSecretsV2, TSecretsV2Insert, TSecretsV2Update } from "@app/db/schemas"; -import { ProjectPermissionSecretActions } from "@app/ee/services/permission/project-permission"; import { OrderByDirection, TProjectPermission } from "@app/lib/types"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { SecretsOrderBy } from "@app/services/secret/secret-types"; @@ -37,8 +36,6 @@ export type TGetSecretsDTO = { includeImports?: boolean; recursive?: boolean; tagSlugs?: string[]; - viewSecretValue: boolean; - throwOnMissingReadValuePermission?: boolean; metadataFilter?: { key?: string; value?: string; @@ -51,11 +48,6 @@ export type TGetSecretsDTO = { keys?: string[]; } & TProjectPermission; -export type TGetSecretsMissingReadValuePermissionDTO = Omit< - TGetSecretsDTO, - "viewSecretValue" | "recursive" | "expandSecretReferences" ->; - export type TGetASecretDTO = { secretName: string; path: string; @@ -65,7 +57,6 @@ export type TGetASecretDTO = { includeImports?: boolean; version?: number; projectId: string; - viewSecretValue: boolean; } & Omit; export type TCreateSecretDTO = TProjectPermission & { @@ -173,9 +164,9 @@ export type TFnSecretBulkInsert = { } >; resourceMetadataDAL: Pick; - secretDAL: Pick; + secretDAL: Pick; secretVersionDAL: Pick; - secretTagDAL: Pick; + secretTagDAL: Pick; secretVersionTagDAL: Pick; }; @@ -197,9 +188,9 @@ export type TFnSecretBulkUpdate = { data: TRequireReferenceIfValue & { tags?: string[]; secretMetadata?: ResourceMetadataDTO }; }[]; resourceMetadataDAL: Pick; - secretDAL: Pick; + secretDAL: Pick; secretVersionDAL: Pick; - secretTagDAL: Pick; + secretTagDAL: Pick; secretVersionTagDAL: Pick; tx?: Knex; }; @@ -341,5 +332,4 @@ export type TGetSecretsRawByFolderMappingsDTO = { folderMappings: { folderId: string; path: string; environment: string }[]; userId: string; filters: TFindSecretsByFolderIdsFilter; - filterByAction?: ProjectPermissionSecretActions; }; diff --git a/backend/src/services/secret-v2-bridge/secret-version-dal.ts b/backend/src/services/secret-v2-bridge/secret-version-dal.ts index f974ab71ec..7772b85183 100644 --- a/backend/src/services/secret-v2-bridge/secret-version-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-version-dal.ts @@ -1,9 +1,9 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretVersionsV2Schema, TableName, TSecretVersionsV2, TSecretVersionsV2Update } from "@app/db/schemas"; +import { TableName, TSecretVersionsV2, TSecretVersionsV2Update } from "@app/db/schemas"; import { BadRequestError, DatabaseError } from "@app/lib/errors"; -import { ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; import { logger } from "@app/lib/logger"; import { QueueName } from "@app/queue"; @@ -12,58 +12,6 @@ export type TSecretVersionV2DALFactory = ReturnType { const secretVersionV2Orm = ormify(db, TableName.SecretVersionV2); - const findBySecretId = async (secretId: string, { offset, limit, sort, tx }: TFindOpt = {}) => { - try { - const query = (tx || db.replicaNode())(TableName.SecretVersionV2) - .where(`${TableName.SecretVersionV2}.secretId`, secretId) - .leftJoin(TableName.SecretV2, `${TableName.SecretVersionV2}.secretId`, `${TableName.SecretV2}.id`) - .leftJoin( - TableName.SecretV2JnTag, - `${TableName.SecretV2}.id`, - `${TableName.SecretV2JnTag}.${TableName.SecretV2}Id` - ) - .leftJoin( - TableName.SecretTag, - `${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`, - `${TableName.SecretTag}.id` - ) - .select(selectAllTableCols(TableName.SecretVersionV2)) - .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) - .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); - - if (limit) void query.limit(limit); - if (offset) void query.offset(offset); - if (sort) { - void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); - } - - const docs = await query; - - const data = sqlNestRelationships({ - data: docs, - key: "id", - parentMapper: (el) => ({ _id: el.id, ...SecretVersionsV2Schema.parse(el) }), - childrenMapper: [ - { - key: "tagId", - label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ - id, - color, - slug, - name: slug - }) - } - ] - }); - - return data; - } catch (error) { - throw new DatabaseError({ error, name: `${TableName.SecretVersionV2}: FindBySecretId` }); - } - }; - // This will fetch all latest secret versions from a folder const findLatestVersionByFolderId = async (folderId: string, tx?: Knex) => { try { @@ -176,7 +124,6 @@ export const secretVersionV2BridgeDALFactory = (db: TDbClient) => { pruneExcessVersions, findLatestVersionMany, bulkUpdate, - findLatestVersionByFolderId, - findBySecretId + findLatestVersionByFolderId }; }; diff --git a/backend/src/services/secret/secret-dal.ts b/backend/src/services/secret/secret-dal.ts index dc41d129f5..cbaf7ddcd3 100644 --- a/backend/src/services/secret/secret-dal.ts +++ b/backend/src/services/secret/secret-dal.ts @@ -169,48 +169,6 @@ export const secretDALFactory = (db: TDbClient) => { } }; - const findManySecretsWithTags = async ( - filter: { - secretIds: string[]; - type: SecretType; - }, - tx?: Knex - ) => { - try { - const secrets = await (tx || db.replicaNode())(TableName.Secret) - .whereIn(`${TableName.Secret}.id` as "id", filter.secretIds) - .where("type", filter.type) - .leftJoin(TableName.JnSecretTag, `${TableName.Secret}.id`, `${TableName.JnSecretTag}.${TableName.Secret}Id`) - .leftJoin(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) - .select(selectAllTableCols(TableName.Secret)) - .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) - .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); - - const data = sqlNestRelationships({ - data: secrets, - key: "id", - parentMapper: (el) => ({ _id: el.id, ...SecretsSchema.parse(el) }), - childrenMapper: [ - { - key: "tagId", - label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ - id, - color, - slug, - name: slug - }) - } - ] - }); - - return data; - } catch (error) { - throw new DatabaseError({ error, name: "get many secrets with tags" }); - } - }; - const findByFolderIds = async (folderIds: string[], userId?: string, tx?: Knex) => { try { // check if not uui then userId id is null (corner case because service token's ID is not UUI in effort to keep backwards compatibility from mongo) @@ -485,7 +443,6 @@ export const secretDALFactory = (db: TDbClient) => { upsertSecretReferences, findReferencedSecretReferences, findAllProjectSecretValues, - pruneSecretReminders, - findManySecretsWithTags + pruneSecretReminders }; }; diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 084aa2ca1e..1775d1f444 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -13,7 +13,7 @@ import { TSecrets } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { ProjectPermissionSecretActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { buildSecretBlindIndexFromName, @@ -51,8 +51,6 @@ import { TUpdateManySecretsRawFnFactory } from "./secret-types"; -export const INFISICAL_SECRET_VALUE_HIDDEN_MASK = ""; - export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => { const appCfg = getConfig(); const secretBlindIndex = await buildSecretBlindIndexFromName({ @@ -192,7 +190,7 @@ export const recursivelyGetSecretPaths = ({ const allowedPaths = paths.filter( (folder) => permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: folder.path @@ -346,7 +344,6 @@ export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderD export const decryptSecretRaw = ( secret: TSecrets & { - secretValueHidden: boolean; workspace: string; environment: string; secretPath: string; @@ -365,14 +362,12 @@ export const decryptSecretRaw = ( key }); - const secretValue = !secret.secretValueHidden - ? decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key - }) - : INFISICAL_SECRET_VALUE_HIDDEN_MASK; + const secretValue = decryptSymmetric128BitHexKeyUTF8({ + ciphertext: secret.secretValueCiphertext, + iv: secret.secretValueIV, + tag: secret.secretValueTag, + key + }); let secretComment = ""; @@ -390,7 +385,6 @@ export const decryptSecretRaw = ( secretPath: secret.secretPath, workspace: secret.workspace, environment: secret.environment, - secretValueHidden: secret.secretValueHidden, secretValue, secretComment, version: secret.version, @@ -1203,23 +1197,3 @@ export const fnDeleteProjectSecretReminders = async ( } } }; - -export const conditionallyHideSecretValue = ( - shouldHideValue: boolean, - { - secretValueCiphertext, - secretValueIV, - secretValueTag - }: { - secretValueCiphertext: string; - secretValueIV: string; - secretValueTag: string; - } -) => { - return { - secretValueCiphertext: shouldHideValue ? INFISICAL_SECRET_VALUE_HIDDEN_MASK : secretValueCiphertext, - secretValueIV: shouldHideValue ? INFISICAL_SECRET_VALUE_HIDDEN_MASK : secretValueIV, - secretValueTag: shouldHideValue ? INFISICAL_SECRET_VALUE_HIDDEN_MASK : secretValueTag, - secretValueHidden: shouldHideValue - }; -}; diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 65e3eaf4f3..00b0e7da84 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -402,8 +402,7 @@ export const secretQueueFactory = ({ expandSecretReferences, secretImportDAL, secretImports, - hasSecretAccess: () => true, - viewSecretValue: true + hasSecretAccess: () => true }); for (let i = importedSecrets.length - 1; i >= 0; i -= 1) { diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 27981a5550..93f68e813d 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -6,7 +6,6 @@ import { ActionProjectType, ProjectMembershipRole, ProjectUpgradeStatus, - ProjectVersion, SecretEncryptionAlgo, SecretKeyEncoding, SecretsSchema, @@ -14,11 +13,7 @@ import { } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal"; import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal"; @@ -53,7 +48,6 @@ import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bri import { TGetSecretReferencesTreeDTO } from "../secret-v2-bridge/secret-v2-bridge-types"; import { TSecretDALFactory } from "./secret-dal"; import { - conditionallyHideSecretValue, decryptSecretRaw, fnSecretBlindIndexCheck, fnSecretBulkDelete, @@ -101,7 +95,7 @@ type TSecretServiceFactoryDep = { projectEnvDAL: Pick; folderDAL: Pick< TSecretFolderDALFactory, - "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" | "findSecretPathByFolderIds" + "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" >; secretV2BridgeService: TSecretV2BridgeServiceFactory; secretBlindIndexDAL: TSecretBlindIndexDALFactory; @@ -210,7 +204,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -328,7 +322,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -450,22 +444,7 @@ export const secretServiceFactory = ({ environmentSlug: folder.environment.slug }); } - - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath: path - }) - ); - - return { - ...updatedSecret[0], - ...conditionallyHideSecretValue(secretValueHidden, updatedSecret[0]), - workspace: projectId, - environment, - secretPath: path - }; + return { ...updatedSecret[0], workspace: projectId, environment, secretPath: path }; }; const deleteSecret = async ({ @@ -488,7 +467,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -561,19 +540,7 @@ export const secretServiceFactory = ({ }); } - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) - ); - - return { - ...deletedSecret[0], - ...conditionallyHideSecretValue(secretValueHidden, deletedSecret[0]), - _id: deletedSecret[0].id, - workspace: projectId, - environment, - secretPath: path - }; + return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment, secretPath: path }; }; const getSecrets = async ({ @@ -622,7 +589,7 @@ export const secretServiceFactory = ({ paths = deepPaths.map(({ folderId, path: p }) => ({ folderId, path: p })); } else { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -647,7 +614,7 @@ export const secretServiceFactory = ({ actor === ActorType.SERVICE ? true : permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: importEnv.slug, secretPath: importPath @@ -704,7 +671,7 @@ export const secretServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); const folder = await folderDAL.findBySecretPath(projectId, environment, path); @@ -754,7 +721,7 @@ export const secretServiceFactory = ({ actor === ActorType.SERVICE ? true : permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment: importEnv.slug, secretPath: importPath @@ -772,7 +739,6 @@ export const secretServiceFactory = ({ if (secretBlindIndex === importedSecrets[i].secrets[j].secretBlindIndex) { return { ...importedSecrets[i].secrets[j], - secretValueHidden: false, workspace: projectId, environment: importedSecrets[i].environment, secretPath: importedSecrets[i].secretPath @@ -783,13 +749,7 @@ export const secretServiceFactory = ({ } if (!secret) throw new NotFoundError({ message: `Secret with name '${secretName}' not found` }); - return { - ...secret, - secretValueHidden: false, // Always false because we check permission at the beginning of the function - workspace: projectId, - environment, - secretPath: path - }; + return { ...secret, workspace: projectId, environment, secretPath: path }; }; const createManySecret = async ({ @@ -811,7 +771,7 @@ export const secretServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -899,7 +859,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -941,8 +901,8 @@ export const secretServiceFactory = ({ if (tagIds.length !== tags.length) throw new NotFoundError({ message: "One or more tags not found" }); const references = await getSecretReference(projectId); - const secrets = await secretDAL.transaction(async (tx) => { - const updatedSecrets = await fnSecretBulkUpdate({ + const secrets = await secretDAL.transaction(async (tx) => + fnSecretBulkUpdate({ folderId, projectId, tx, @@ -972,18 +932,8 @@ export const secretServiceFactory = ({ secretVersionDAL, secretTagDAL, secretVersionTagDAL - }); - - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) - ); - - return updatedSecrets.map((secret) => ({ - ...secret, - ...conditionallyHideSecretValue(secretValueHidden, secret) - })); - }); + }) + ); await snapshotService.performSnapshot(folderId); await secretQueueService.syncSecrets({ @@ -1017,7 +967,7 @@ export const secretServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); @@ -1069,15 +1019,7 @@ export const secretServiceFactory = ({ } } - const secretValueHidden = !permission.can( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) - ); - - return secrets.map((secret) => ({ - ...secret, - ...conditionallyHideSecretValue(secretValueHidden, secret) - })); + return secrets; }); await snapshotService.performSnapshot(folderId); @@ -1238,7 +1180,6 @@ export const secretServiceFactory = ({ secretName, path: secretPath, environment, - viewSecretValue: false, type: "shared" }); @@ -1253,11 +1194,10 @@ export const secretServiceFactory = ({ | (typeof groupPermissions)[number] ) => { const allowedActions = [ - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSecretActions.ReadValue, - ProjectPermissionSecretActions.Delete, - ProjectPermissionSecretActions.Create, - ProjectPermissionSecretActions.Edit + ProjectPermissionActions.Read, + ProjectPermissionActions.Delete, + ProjectPermissionActions.Create, + ProjectPermissionActions.Edit ].filter((action) => entityPermission.permission.can( action, @@ -1294,13 +1234,11 @@ export const secretServiceFactory = ({ actorId, actorOrgId, actorAuthMethod, - viewSecretValue, environment, includeImports, expandSecretReferences, recursive, tagSlugs = [], - throwOnMissingReadValuePermission = true, ...paramsV2 }: TGetSecretsRawDTO) => { const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId); @@ -1311,8 +1249,6 @@ export const secretServiceFactory = ({ actorId, actor, actorOrgId, - viewSecretValue, - throwOnMissingReadValuePermission, environment, path, recursive, @@ -1321,7 +1257,6 @@ export const secretServiceFactory = ({ tagSlugs, ...paramsV2 }); - return { secrets, imports }; } @@ -1350,20 +1285,14 @@ export const secretServiceFactory = ({ recursive }); - const decryptedSecrets = secrets.map((el) => decryptSecretRaw({ ...el, secretValueHidden: false }, botKey)); + const decryptedSecrets = secrets.map((el) => decryptSecretRaw(el, botKey)); const filteredSecrets = tagSlugs.length ? decryptedSecrets.filter((secret) => Boolean(secret.tags?.find((el) => tagSlugs.includes(el.slug)))) : decryptedSecrets; const processedImports = (imports || [])?.map(({ secrets: importedSecrets, ...el }) => { const decryptedImportSecrets = importedSecrets.map((sec) => decryptSecretRaw( - { - ...sec, - environment: el.environment, - workspace: projectId, - secretPath: el.secretPath, - secretValueHidden: false - }, + { ...sec, environment: el.environment, workspace: projectId, secretPath: el.secretPath }, botKey ) ); @@ -1374,7 +1303,6 @@ export const secretServiceFactory = ({ const importedEntries = decryptedImportSecrets.reduce( ( accum: { - secretValueHidden: boolean; secretKey: string; secretPath: string; workspace: string; @@ -1418,7 +1346,6 @@ export const secretServiceFactory = ({ Object.keys(secretsGroupByPath).map((groupedPath) => Promise.allSettled( secretsGroupByPath[groupedPath].map(async (decryptedSecret, index) => { - if (decryptedSecret.secretValueHidden) return; const expandedSecretValue = await expandSecret({ value: decryptedSecret.secretValue, secretPath: groupedPath, @@ -1435,7 +1362,6 @@ export const secretServiceFactory = ({ processedImports.map((processedImport) => Promise.allSettled( processedImport.secrets.map(async (decryptedSecret, index) => { - if (decryptedSecret.secretValueHidden) return; const expandedSecretValue = await expandSecret({ value: decryptedSecret.secretValue, secretPath: path, @@ -1461,7 +1387,6 @@ export const secretServiceFactory = ({ path, actor, environment, - viewSecretValue, projectId: workspaceId, expandSecretReferences, projectSlug, @@ -1481,7 +1406,6 @@ export const secretServiceFactory = ({ includeImports, actorAuthMethod, path, - viewSecretValue, actorOrgId, actor, actorId, @@ -1512,7 +1436,6 @@ export const secretServiceFactory = ({ message: `Project bot for project with ID '${projectId}' not found. Please upgrade your project.`, name: "bot_not_found_error" }); - const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey); if (expandSecretReferences) { @@ -1531,10 +1454,7 @@ export const secretServiceFactory = ({ decryptedSecret.secretValue = expandedSecretValue || ""; } - return { - secretMetadata: undefined, - ...decryptedSecret - }; + return { secretMetadata: undefined, ...decryptedSecret }; }; const createSecretRaw = async ({ @@ -1685,16 +1605,7 @@ export const secretServiceFactory = ({ tags: tagIds }); - return { - type: SecretProtectionType.Direct as const, - secret: decryptSecretRaw( - { - ...secret, - secretValueHidden: false - }, - botKey - ) - }; + return { type: SecretProtectionType.Direct as const, secret: decryptSecretRaw(secret, botKey) }; }; const updateSecretRaw = async ({ @@ -2090,7 +2001,7 @@ export const secretServiceFactory = ({ return { type: SecretProtectionType.Direct as const, secrets: secrets.map((secret) => - decryptSecretRaw({ ...secret, workspace: projectId, environment, secretPath, secretValueHidden: false }, botKey) + decryptSecretRaw({ ...secret, workspace: projectId, environment, secretPath }, botKey) ) }; }; @@ -2379,12 +2290,6 @@ export const secretServiceFactory = ({ const folder = await folderDAL.findById(secret.folderId); if (!folder) throw new NotFoundError({ message: `Folder with ID '${secret.folderId}' not found` }); - const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(folder.projectId, [folder.id]); - - if (!folderWithPath) { - throw new NotFoundError({ message: `Folder with ID '${folder.id}' not found` }); - } - const { botKey } = await projectBotService.getBotKey(folder.projectId); if (!botKey) throw new NotFoundError({ message: `Project bot for project with ID '${folder.projectId}' not found` }); @@ -2398,42 +2303,18 @@ export const secretServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - const secretVersions = await secretVersionDAL.findBySecretId(secretId, { - offset, - limit, - sort: [["createdAt", "desc"]] - }); - return secretVersions.map((el) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretKeyCiphertext, - iv: secret.secretKeyIV, - tag: secret.secretKeyTag, - key: botKey - }); - - const secretValueHidden = permission.cannot( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment: folder.environment.envSlug, - secretPath: folderWithPath.path, - secretName: secretKey, - ...(el.tags?.length && { - secretTags: el.tags.map((tag) => tag.slug) - }) - }) - ); - - return decryptSecretRaw( + const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] }); + return secretVersions.map((el) => + decryptSecretRaw( { - secretValueHidden, ...el, workspace: folder.projectId, environment: folder.environment.envSlug, - secretPath: folderWithPath.path + secretPath: "/" }, botKey - ); - }); + ) + ); }; const attachTags = async ({ @@ -2459,7 +2340,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); @@ -2565,7 +2446,7 @@ export const secretServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); @@ -2731,7 +2612,7 @@ export const secretServiceFactory = ({ message: `Project with slug '${projectSlug}' not found` }); } - if (project.version === ProjectVersion.V3) { + if (project.version === 3) { return secretV2BridgeService.moveSecrets({ sourceEnvironment, sourceSecretPath, @@ -2756,6 +2637,30 @@ export const secretServiceFactory = ({ actionProjectType: ActionProjectType.SecretManager }); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Delete, + subject(ProjectPermissionSub.Secrets, { + environment: sourceEnvironment, + secretPath: sourceSecretPath + }) + ); + + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Create, + subject(ProjectPermissionSub.Secrets, { + environment: destinationEnvironment, + secretPath: destinationSecretPath + }) + ); + + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Edit, + subject(ProjectPermissionSub.Secrets, { + environment: destinationEnvironment, + secretPath: destinationSecretPath + }) + ); + const { botKey } = await projectBotService.getBotKey(project.id); if (!botKey) { throw new NotFoundError({ @@ -2783,9 +2688,11 @@ export const secretServiceFactory = ({ }); } - const sourceSecrets = await secretDAL.findManySecretsWithTags({ + const sourceSecrets = await secretDAL.find({ type: SecretType.Shared, - secretIds + $in: { + id: secretIds + } }); if (sourceSecrets.length !== secretIds.length) { @@ -2794,52 +2701,21 @@ export const secretServiceFactory = ({ }); } - const sourceActions = [ - ProjectPermissionSecretActions.Delete, - ProjectPermissionSecretActions.DescribeSecret, - ProjectPermissionSecretActions.ReadValue - ] as const; - const destinationActions = [ProjectPermissionSecretActions.Create, ProjectPermissionSecretActions.Edit] as const; - - const decryptedSourceSecrets = sourceSecrets.map((secret) => { - const secretKey = decryptSymmetric128BitHexKeyUTF8({ + const decryptedSourceSecrets = sourceSecrets.map((secret) => ({ + ...secret, + secretKey: decryptSymmetric128BitHexKeyUTF8({ ciphertext: secret.secretKeyCiphertext, iv: secret.secretKeyIV, tag: secret.secretKeyTag, key: botKey - }); - - for (const destinationAction of destinationActions) { - ForbiddenError.from(permission).throwUnlessCan( - destinationAction, - subject(ProjectPermissionSub.Secrets, { - environment: destinationEnvironment, - secretPath: destinationSecretPath - }) - ); - } - - for (const sourceAction of sourceActions) { - ForbiddenError.from(permission).throwUnlessCan( - sourceAction, - subject(ProjectPermissionSub.Secrets, { - environment: sourceEnvironment, - secretPath: sourceSecretPath - }) - ); - } - - return { - ...secret, - secretKey, - secretValue: decryptSymmetric128BitHexKeyUTF8({ - ciphertext: secret.secretValueCiphertext, - iv: secret.secretValueIV, - tag: secret.secretValueTag, - key: botKey - }) - }; - }); + }), + secretValue: decryptSymmetric128BitHexKeyUTF8({ + ciphertext: secret.secretValueCiphertext, + iv: secret.secretValueIV, + tag: secret.secretValueTag, + key: botKey + }) + })); let isSourceUpdated = false; let isDestinationUpdated = false; diff --git a/backend/src/services/secret/secret-types.ts b/backend/src/services/secret/secret-types.ts index 8ac9f58c0e..1586052763 100644 --- a/backend/src/services/secret/secret-types.ts +++ b/backend/src/services/secret/secret-types.ts @@ -180,8 +180,6 @@ export type TGetSecretsRawDTO = { expandSecretReferences?: boolean; path: string; environment: string; - viewSecretValue: boolean; - throwOnMissingReadValuePermission?: boolean; includeImports?: boolean; recursive?: boolean; tagSlugs?: string[]; @@ -207,7 +205,6 @@ export type TGetASecretRawDTO = { secretName: string; path: string; environment: string; - viewSecretValue: boolean; expandSecretReferences?: boolean; type: "shared" | "personal"; includeImports?: boolean; @@ -412,7 +409,7 @@ export type TCreateManySecretsRawFnFactory = { kmsService: Pick; secretV2BridgeDAL: Pick< TSecretV2BridgeDALFactory, - "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find" + "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" >; secretVersionV2BridgeDAL: Pick; secretVersionTagV2BridgeDAL: Pick; @@ -449,7 +446,7 @@ export type TUpdateManySecretsRawFnFactory = { kmsService: Pick; secretV2BridgeDAL: Pick< TSecretV2BridgeDALFactory, - "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find" + "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" >; secretVersionV2BridgeDAL: Pick; secretVersionTagV2BridgeDAL: Pick; diff --git a/backend/src/services/secret/secret-version-dal.ts b/backend/src/services/secret/secret-version-dal.ts index 8e4544c19e..8e77858a53 100644 --- a/backend/src/services/secret/secret-version-dal.ts +++ b/backend/src/services/secret/secret-version-dal.ts @@ -1,9 +1,9 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretVersionsSchema, TableName, TSecretVersions, TSecretVersionsUpdate } from "@app/db/schemas"; +import { TableName, TSecretVersions, TSecretVersionsUpdate } from "@app/db/schemas"; import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; -import { ormify, selectAllTableCols, sqlNestRelationships, TFindOpt } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; import { logger } from "@app/lib/logger"; import { QueueName } from "@app/queue"; @@ -12,50 +12,6 @@ export type TSecretVersionDALFactory = ReturnType { const secretVersionOrm = ormify(db, TableName.SecretVersion); - const findBySecretId = async (secretId: string, { offset, limit, sort, tx }: TFindOpt = {}) => { - try { - const query = (tx || db.replicaNode())(TableName.SecretVersion) - .where(`${TableName.SecretVersion}.secretId`, secretId) - .leftJoin(TableName.Secret, `${TableName.SecretVersion}.secretId`, `${TableName.Secret}.id`) - .leftJoin(TableName.JnSecretTag, `${TableName.Secret}.id`, `${TableName.JnSecretTag}.${TableName.Secret}Id`) - .leftJoin(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) - .select(selectAllTableCols(TableName.SecretVersion)) - .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) - .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) - .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")); - - if (limit) void query.limit(limit); - if (offset) void query.offset(offset); - if (sort) { - void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); - } - - const docs = await query; - - const data = sqlNestRelationships({ - data: docs, - key: "id", - parentMapper: (el) => ({ _id: el.id, ...SecretVersionsSchema.parse(el) }), - childrenMapper: [ - { - key: "tagId", - label: "tags" as const, - mapper: ({ tagId: id, tagColor: color, tagSlug: slug }) => ({ - id, - color, - slug, - name: slug - }) - } - ] - }); - - return data; - } catch (error) { - throw new DatabaseError({ error, name: `${TableName.SecretVersion}: FindBySecretId` }); - } - }; - // This will fetch all latest secret versions from a folder const findLatestVersionByFolderId = async (folderId: string, tx?: Knex) => { try { @@ -193,7 +149,6 @@ export const secretVersionDALFactory = (db: TDbClient) => { findLatestVersionMany, bulkUpdate, findLatestVersionByFolderId, - findBySecretId, bulkUpdateNoVersionIncrement }; }; diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 9b87c29f89..654917febd 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -5,11 +5,7 @@ import bcrypt from "bcrypt"; import { ActionProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSecretActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors"; @@ -71,7 +67,7 @@ export const serviceTokenServiceFactory = ({ scopes.forEach(({ environment, secretPath }) => { ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); }); diff --git a/backend/src/services/service-token/service-token-types.ts b/backend/src/services/service-token/service-token-types.ts index d06d90a9d9..f1a700908b 100644 --- a/backend/src/services/service-token/service-token-types.ts +++ b/backend/src/services/service-token/service-token-types.ts @@ -7,7 +7,7 @@ export type TCreateServiceTokenDTO = { iv: string; tag: string; expiresIn?: number | null; - permissions: ("read" | "write" | "readValue")[]; + permissions: ("read" | "write")[]; } & TProjectPermission; export type TGetServiceTokenInfoDTO = Omit; diff --git a/frontend/src/components/secrets/SecretReferenceDetails/SecretReferenceDetails.tsx b/frontend/src/components/secrets/SecretReferenceDetails/SecretReferenceDetails.tsx index b8724f5dc3..8164353d83 100644 --- a/frontend/src/components/secrets/SecretReferenceDetails/SecretReferenceDetails.tsx +++ b/frontend/src/components/secrets/SecretReferenceDetails/SecretReferenceDetails.tsx @@ -1,21 +1,13 @@ -/* eslint-disable no-nested-ternary */ -import { useEffect, useState } from "react"; -import { - faChevronRight, - faExclamationTriangle, - faEye, - faEyeSlash -} from "@fortawesome/free-solid-svg-icons"; +import { useState } from "react"; +import { faChevronRight, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as Collapsible from "@radix-ui/react-collapsible"; -import { AxiosError } from "axios"; import { twMerge } from "tailwind-merge"; -import { createNotification } from "@app/components/notifications"; import { FormControl, FormLabel, SecretInput, Spinner, Tooltip } from "@app/components/v2"; import { useWorkspace } from "@app/context"; import { useGetSecretReferenceTree } from "@app/hooks/api"; -import { ApiErrorTypes, TApiErrors, TSecretReferenceTraceNode } from "@app/hooks/api/types"; +import { TSecretReferenceTraceNode } from "@app/hooks/api/types"; import style from "./SecretReferenceDetails.module.css"; @@ -92,7 +84,7 @@ export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Prop const { currentWorkspace } = useWorkspace(); const projectId = currentWorkspace?.id || ""; - const { data, isPending, isError, error } = useGetSecretReferenceTree({ + const { data, isPending } = useGetSecretReferenceTree({ secretPath, environmentSlug: environment, projectId, @@ -102,26 +94,6 @@ export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Prop const tree = data?.tree; const secretValue = data?.value; - useEffect(() => { - if (error instanceof AxiosError) { - const err = error?.response?.data as TApiErrors; - - if (err?.error === ApiErrorTypes.CustomForbiddenError) { - createNotification({ - title: "You don't have permission to view reference tree", - text: "You don't have permission to view one or more of the referenced secrets.", - type: "error" - }); - return; - } - createNotification({ - title: "Error fetching secret reference tree", - text: "Please try again later.", - type: "error" - }); - } - }, [error]); - if (isPending) { return (
@@ -142,16 +114,11 @@ export const SecretReferenceTree = ({ secretPath, environment, secretKey }: Prop
- {isError ? ( -
- -

Error fetching secret reference tree

-
- ) : tree ? ( + {tree && (
- ) : null} + )}
Click a secret key to view its sub-references. diff --git a/frontend/src/components/v2/Blur/Blur.tsx b/frontend/src/components/v2/Blur/Blur.tsx deleted file mode 100644 index bd1ded40a8..0000000000 --- a/frontend/src/components/v2/Blur/Blur.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { twMerge } from "tailwind-merge"; - -import { Tooltip } from "../Tooltip/Tooltip"; - -interface IProps { - className?: string; - tooltipText?: string; -} - -export const Blur = ({ className, tooltipText }: IProps) => { - return ( - -
- ******** -
-
- ); -}; diff --git a/frontend/src/components/v2/Blur/index.tsx b/frontend/src/components/v2/Blur/index.tsx deleted file mode 100644 index 50fb7ccc64..0000000000 --- a/frontend/src/components/v2/Blur/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Blur } from "./Blur"; diff --git a/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx b/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx index dd6d3575e3..6fbe962389 100644 --- a/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx +++ b/frontend/src/components/v2/InfisicalSecretInput/InfisicalSecretInput.tsx @@ -120,7 +120,6 @@ export const InfisicalSecretInput = forwardRef( const isPopupOpen = Boolean(suggestionSource.isOpen) && isFocused; const { data: secrets } = useGetProjectSecrets({ - viewSecretValue: false, environment: suggestionSource.environment || "", secretPath: suggestionSource.secretPath || "", workspaceId, diff --git a/frontend/src/context/ProjectPermissionContext/types.ts b/frontend/src/context/ProjectPermissionContext/types.ts index 260ffce093..d368a949f5 100644 --- a/frontend/src/context/ProjectPermissionContext/types.ts +++ b/frontend/src/context/ProjectPermissionContext/types.ts @@ -7,14 +7,6 @@ export enum ProjectPermissionActions { Delete = "delete" } -export enum ProjectPermissionSecretActions { - DescribeSecret = "read", - ReadValue = "readValue", - Create = "create", - Edit = "edit", - Delete = "delete" -} - export enum ProjectPermissionDynamicSecretActions { ReadRootCredential = "read-root-credential", CreateRootCredential = "create-root-credential", @@ -146,7 +138,7 @@ export type SecretImportSubjectFields = { export type ProjectPermissionSet = | [ - ProjectPermissionSecretActions, + ProjectPermissionActions, ( | ProjectPermissionSub.Secrets | (ForcedSubject & SecretSubjectFields) diff --git a/frontend/src/hooks/api/dashboard/queries.tsx b/frontend/src/hooks/api/dashboard/queries.tsx index f704081a9f..4b63be9883 100644 --- a/frontend/src/hooks/api/dashboard/queries.tsx +++ b/frontend/src/hooks/api/dashboard/queries.tsx @@ -207,7 +207,6 @@ export const useGetProjectSecretsDetails = ( search = "", includeSecrets, includeFolders, - viewSecretValue, includeImports, includeDynamicSecrets, tags @@ -232,7 +231,6 @@ export const useGetProjectSecretsDetails = ( limit, orderBy, orderDirection, - viewSecretValue, offset, projectId, environment, @@ -249,7 +247,6 @@ export const useGetProjectSecretsDetails = ( limit, orderBy, orderDirection, - viewSecretValue, offset, projectId, environment, diff --git a/frontend/src/hooks/api/dashboard/types.ts b/frontend/src/hooks/api/dashboard/types.ts index 4808b463c6..786b2b43a1 100644 --- a/frontend/src/hooks/api/dashboard/types.ts +++ b/frontend/src/hooks/api/dashboard/types.ts @@ -69,7 +69,6 @@ export type TGetDashboardProjectSecretsDetailsDTO = Omit< TGetDashboardProjectSecretsOverviewDTO, "environments" > & { - viewSecretValue: boolean; environment: string; includeImports?: boolean; tags: Record; diff --git a/frontend/src/hooks/api/secretApprovalRequest/queries.tsx b/frontend/src/hooks/api/secretApprovalRequest/queries.tsx index 39fa1058af..74acf744bd 100644 --- a/frontend/src/hooks/api/secretApprovalRequest/queries.tsx +++ b/frontend/src/hooks/api/secretApprovalRequest/queries.tsx @@ -79,7 +79,6 @@ export const decryptSecrets = ( id: encSecret.id, env: encSecret.environment, key: secretKey, - secretValueHidden: encSecret.secretValueHidden, value: secretValue, tags: encSecret.tags, comment: secretComment, diff --git a/frontend/src/hooks/api/secretImports/queries.tsx b/frontend/src/hooks/api/secretImports/queries.tsx index dfc8c64978..c2ddd2fbe2 100644 --- a/frontend/src/hooks/api/secretImports/queries.tsx +++ b/frontend/src/hooks/api/secretImports/queries.tsx @@ -137,7 +137,6 @@ export const useGetImportedSecretsSingleEnv = ({ env: encSecret.environment, key: encSecret.secretKey, value: encSecret.secretValue, - secretValueHidden: encSecret.secretValueHidden, tags: encSecret.tags, comment: encSecret.secretComment, createdAt: encSecret.createdAt, @@ -177,7 +176,6 @@ export const useGetImportedSecretsAllEnvs = ({ env: encSecret.environment, key: encSecret.secretKey, value: encSecret.secretValue, - secretValueHidden: encSecret.secretValueHidden, tags: encSecret.tags, comment: encSecret.secretComment, createdAt: encSecret.createdAt, diff --git a/frontend/src/hooks/api/secretSnapshots/queries.tsx b/frontend/src/hooks/api/secretSnapshots/queries.tsx index d273bd5ea3..8a16b41806 100644 --- a/frontend/src/hooks/api/secretSnapshots/queries.tsx +++ b/frontend/src/hooks/api/secretSnapshots/queries.tsx @@ -75,7 +75,6 @@ export const useGetSnapshotSecrets = ({ snapshotId }: TSnapshotDataProps) => id: secretVersion.secretId, env: data.environment.slug, key: secretVersion.secretKey, - secretValueHidden: secretVersion.secretValueHidden, value: secretVersion.secretValue || "", tags: secretVersion.tags, comment: secretVersion.secretComment, diff --git a/frontend/src/hooks/api/secrets/queries.tsx b/frontend/src/hooks/api/secrets/queries.tsx index 3e82c3b907..a6dd959b07 100644 --- a/frontend/src/hooks/api/secrets/queries.tsx +++ b/frontend/src/hooks/api/secrets/queries.tsx @@ -26,13 +26,8 @@ import { export const secretKeys = { // this is also used in secretSnapshot part - getProjectSecret: ({ - workspaceId, - environment, - secretPath, - viewSecretValue - }: TGetProjectSecretsKey) => - [{ workspaceId, environment, secretPath, viewSecretValue }, "secrets"] as const, + getProjectSecret: ({ workspaceId, environment, secretPath }: TGetProjectSecretsKey) => + [{ workspaceId, environment, secretPath }, "secrets"] as const, getSecretVersion: (secretId: string) => [{ secretId }, "secret-versions"] as const, getSecretAccessList: ({ workspaceId, @@ -49,15 +44,13 @@ export const fetchProjectSecrets = async ({ environment, secretPath, includeImports, - expandSecretReferences, - viewSecretValue + expandSecretReferences }: TGetProjectSecretsKey) => { const { data } = await apiRequest.get("/api/v3/secrets/raw", { params: { environment, workspaceId, secretPath, - viewSecretValue, expandSecretReferences, include_imports: includeImports } @@ -75,7 +68,6 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => { env: el.environment, key: el.secretKey, value: el.secretValue, - secretValueHidden: el.secretValueHidden, tags: el.tags || [], comment: el.secretComment || "", reminderRepeatDays: el.secretReminderRepeatDays, @@ -115,7 +107,6 @@ export const useGetProjectSecrets = ({ workspaceId, environment, secretPath, - viewSecretValue, options }: TGetProjectSecretsDTO & { options?: Omit< @@ -132,13 +123,8 @@ export const useGetProjectSecrets = ({ ...options, // wait for all values to be available enabled: Boolean(workspaceId && environment) && (options?.enabled ?? true), - queryKey: secretKeys.getProjectSecret({ - workspaceId, - environment, - secretPath, - viewSecretValue - }), - queryFn: () => fetchProjectSecrets({ workspaceId, environment, secretPath, viewSecretValue }), + queryKey: secretKeys.getProjectSecret({ workspaceId, environment, secretPath }), + queryFn: () => fetchProjectSecrets({ workspaceId, environment, secretPath }), select: useCallback( (data: Awaited>) => mergePersonalSecrets(data.secrets), [] diff --git a/frontend/src/hooks/api/secrets/types.ts b/frontend/src/hooks/api/secrets/types.ts index d7b6cbcad4..92dc220b81 100644 --- a/frontend/src/hooks/api/secrets/types.ts +++ b/frontend/src/hooks/api/secrets/types.ts @@ -19,7 +19,6 @@ export type EncryptedSecret = { secretValueCiphertext: string; secretValueIV: string; secretValueTag: string; - secretValueHidden: boolean; __v: number; createdAt: string; updatedAt: string; @@ -38,7 +37,6 @@ export type SecretV3RawSanitized = { version: number; key: string; value?: string; - secretValueHidden: boolean; comment?: string; reminderRepeatDays?: number | null; reminderNote?: string | null; @@ -63,7 +61,6 @@ export type SecretV3Raw = { environment: string; version: number; type: string; - secretValueHidden: boolean; secretKey: string; secretPath: string; secretValue?: string; @@ -98,7 +95,6 @@ export type SecretVersions = { envId: string; secretKey: string; secretValue?: string; - secretValueHidden: boolean; secretComment?: string; tags: WsTag[]; __v: number; @@ -113,7 +109,6 @@ export type TGetProjectSecretsKey = { environment: string; secretPath?: string; includeImports?: boolean; - viewSecretValue?: boolean; expandSecretReferences?: boolean; }; diff --git a/frontend/src/hooks/api/types.ts b/frontend/src/hooks/api/types.ts index 029d8a6056..c03358b42f 100644 --- a/frontend/src/hooks/api/types.ts +++ b/frontend/src/hooks/api/types.ts @@ -46,8 +46,7 @@ export enum ApiErrorTypes { ValidationError = "ValidationFailure", BadRequestError = "BadRequest", UnauthorizedError = "UnauthorizedError", - ForbiddenError = "PermissionDenied", - CustomForbiddenError = "ForbiddenError" + ForbiddenError = "PermissionDenied" } export type TApiErrors = @@ -70,12 +69,6 @@ export type TApiErrors = details: PureAbility["rules"]; statusCode: 403; } - | { - reqId: string; - error: ApiErrorTypes.CustomForbiddenError; - message: string; - statusCode: 403; - } | { reqId: string; statusCode: 400; diff --git a/frontend/src/pages/project/AccessControlPage/components/ServiceTokenTab/components/ServiceTokenSection/AddServiceTokenModal.tsx b/frontend/src/pages/project/AccessControlPage/components/ServiceTokenTab/components/ServiceTokenSection/AddServiceTokenModal.tsx index a669a83602..46a9c56804 100644 --- a/frontend/src/pages/project/AccessControlPage/components/ServiceTokenTab/components/ServiceTokenSection/AddServiceTokenModal.tsx +++ b/frontend/src/pages/project/AccessControlPage/components/ServiceTokenTab/components/ServiceTokenSection/AddServiceTokenModal.tsx @@ -58,7 +58,6 @@ const schema = z.object({ permissions: z .object({ read: z.boolean(), - readValue: z.boolean(), write: z.boolean() }) .required() @@ -297,19 +296,14 @@ export const AddServiceTokenModal = ({ popUp, handlePopUpToggle }: Props) => { name="permissions" defaultValue={{ read: true, - readValue: false, write: false }} render={({ field: { onChange, value }, fieldState: { error } }) => { const options = [ { - label: "Describe Secret (default)", + label: "Read (default)", value: "read" }, - { - label: "Read Value (optional)", - value: "readValue" - }, { label: "Write (optional)", value: "write" diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies.tsx index 8aca40a345..de2231fc93 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/GeneralPermissionPolicies.tsx @@ -117,27 +117,24 @@ export const GeneralPermissionPolicies = {actions.map(({ label, value }) => { if (typeof value !== "string") return undefined; - return ( { - return ( -
- - {label} - -
- ); - }} + render={({ field }) => ( +
+ + {label} + +
+ )} /> ); })} diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx index e26ec8bc41..7dcb118fd7 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx @@ -9,7 +9,6 @@ import { PermissionConditionOperators, ProjectPermissionDynamicSecretActions, ProjectPermissionKmipActions, - ProjectPermissionSecretActions, ProjectPermissionSecretSyncActions, TPermissionCondition, TPermissionConditionOperators @@ -23,14 +22,6 @@ const GeneralPolicyActionSchema = z.object({ create: z.boolean().optional() }); -const SecretPolicyActionSchema = z.object({ - read: z.boolean().optional(), // describe secret - edit: z.boolean().optional(), - delete: z.boolean().optional(), - create: z.boolean().optional(), - readValue: z.boolean().optional() -}); - const CmekPolicyActionSchema = z.object({ read: z.boolean().optional(), edit: z.boolean().optional(), @@ -123,7 +114,7 @@ export const projectRoleFormSchema = z.object({ .refine((val) => val !== "custom", { message: "Cannot use custom as its a keyword" }), permissions: z .object({ - [ProjectPermissionSub.Secrets]: SecretPolicyActionSchema.extend({ + [ProjectPermissionSub.Secrets]: GeneralPolicyActionSchema.extend({ inverted: z.boolean().optional(), conditions: ConditionSchema }) @@ -292,28 +283,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { }); return; } - - if (subject === ProjectPermissionSub.Secrets) { - const canRead = action.includes(ProjectPermissionSecretActions.DescribeSecret); - const canEdit = action.includes(ProjectPermissionSecretActions.Edit); - const canDelete = action.includes(ProjectPermissionSecretActions.Delete); - const canCreate = action.includes(ProjectPermissionSecretActions.Create); - const canReadValue = action.includes(ProjectPermissionSecretActions.ReadValue); - - // from above statement we are sure it won't be undefined - formVal[subject]!.push({ - read: canRead, - create: canCreate, - edit: canEdit, - delete: canDelete, - readValue: canReadValue, - conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [], - inverted - }); - - return; - } - // for other subjects const canRead = action.includes(ProjectPermissionActions.Read); const canEdit = action.includes(ProjectPermissionActions.Edit); @@ -514,9 +483,8 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { [ProjectPermissionSub.Secrets]: { title: "Secrets", actions: [ - { label: "Describe Secret", value: "read" }, + { label: "Read", value: "read" }, { label: "Create", value: "create" }, - { label: "Read Value", value: "readValue" }, { label: "Modify", value: "edit" }, { label: "Remove", value: "delete" } ] diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx index 4847feb69f..c894d6b14e 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -19,7 +19,6 @@ import { import { getKeyValue } from "@app/helpers/parseEnvVar"; import { useCreateFolder, useCreateSecretV3, useCreateWsTag, useGetWsTags } from "@app/hooks/api"; import { SecretType } from "@app/hooks/api/types"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; const typeSchema = z .object({ @@ -276,7 +275,7 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { isMulti options={environments.filter((environment) => permission.can( - ProjectPermissionSecretActions.Create, + ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment: environment.slug, secretPath, diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretEditRow.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretEditRow.tsx index 4887d4fd77..d3dfd49e75 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretEditRow.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretEditRow.tsx @@ -25,7 +25,6 @@ import { ModalTrigger, Tooltip } from "@app/components/v2"; -import { Blur } from "@app/components/v2/Blur"; import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { useToggle } from "@app/hooks"; @@ -40,7 +39,6 @@ type Props = { isVisible?: boolean; isImportedSecret: boolean; environment: string; - secretValueHidden: boolean; secretPath: string; onSecretCreate: (env: string, key: string, value: string) => Promise; onSecretUpdate: ( @@ -60,7 +58,6 @@ export const SecretEditRow = ({ isImportedSecret, onSecretUpdate, secretName, - secretValueHidden, onSecretCreate, onSecretDelete, environment, @@ -143,29 +140,24 @@ export const SecretEditRow = ({ />
- {secretValueHidden ? ( - - ) : ( - ( - - )} - /> - )} + ( + + )} + />
-
- +
NO ACCESS
diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx index 3a66f68961..b9376f6b47 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx @@ -221,13 +221,10 @@ export const SecretOverviewTableRow = ({ secretPath={secretPath} isVisible={isSecretVisible} secretName={secretKey} - secretValueHidden={secret?.secretValueHidden || false} defaultValue={ - secret?.secretValueHidden - ? "" - : secret?.valueOverride || - secret?.value || - importedSecret?.secret?.value + secret?.valueOverride || + secret?.value || + importedSecret?.secret?.value } secretId={secret?.id} isOverride={Boolean(secret?.valueOverride)} diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx index 3b05f10fa8..ae1916b86a 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx @@ -10,8 +10,12 @@ import { z } from "zod"; import { createNotification } from "@app/components/notifications"; import { IconButton, Input, Spinner, Tooltip } from "@app/components/v2"; -import { ProjectPermissionSub, useProjectPermission, useWorkspace } from "@app/context"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; +import { + ProjectPermissionActions, + ProjectPermissionSub, + useProjectPermission, + useWorkspace +} from "@app/context"; import { useToggle } from "@app/hooks"; import { useUpdateSecretV3 } from "@app/hooks/api"; import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/types"; @@ -51,8 +55,8 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath } secretTags: (secretDetails?.tags || []).map((i) => i.slug) }); const isSecretInEnvReadOnly = - permission.can(ProjectPermissionSecretActions.DescribeSecret, secretPermissionSubject) && - permission.cannot(ProjectPermissionSecretActions.Edit, secretPermissionSubject); + permission.can(ProjectPermissionActions.Read, secretPermissionSubject) && + permission.cannot(ProjectPermissionActions.Edit, secretPermissionSubject); if (isSecretInEnvReadOnly) { return true; } diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SecretSearchInput/components/QuickSearchSecretItem.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SecretSearchInput/components/QuickSearchSecretItem.tsx index 9943de5b61..e901a4ff7f 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SecretSearchInput/components/QuickSearchSecretItem.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SecretSearchInput/components/QuickSearchSecretItem.tsx @@ -110,31 +110,21 @@ export const QuickSearchSecretItem = ({ )} {isSingleEnv ? ( - { + e.stopPropagation(); + const el = envSlugMap.get(groupSecret.env)?.name; + if (el) { + handleCopy(groupSecret.value!, el); + } + }} > - { - e.stopPropagation(); - const el = envSlugMap.get(groupSecret.env)?.name; - if (el) { - handleCopy(groupSecret.value!, el); - } - }} - > - - - + + ) : ( @@ -168,24 +158,14 @@ export const QuickSearchSecretItem = ({ )} - - - - - + + Hover to Reveal... diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx index 4e048b9413..4738e411ee 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/SelectionPanel.tsx @@ -11,7 +11,6 @@ import { useProjectPermission, useWorkspace } from "@app/context"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; import { usePopUp } from "@app/hooks"; import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api"; import { @@ -59,7 +58,7 @@ export const SelectionPanel = ({ secretPath, resetSelectedEntries, selectedEntri // user should have the ability to delete secrets/folders in at least one of the envs const shouldShowDelete = userAvailableEnvs.some((env) => permission.can( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath, @@ -111,7 +110,7 @@ export const SelectionPanel = ({ secretPath, resetSelectedEntries, selectedEntri (accum: TDeleteSecretBatchDTO["secrets"], secretRecord) => { const entry = secretRecord[env.slug]; const canDeleteSecret = permission.can( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath, diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/components/MoveSecretsDialog/MoveSecretsDialog.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/components/MoveSecretsDialog/MoveSecretsDialog.tsx index a81fa3e53e..8a27a24c01 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/components/MoveSecretsDialog/MoveSecretsDialog.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SelectionPanel/components/MoveSecretsDialog/MoveSecretsDialog.tsx @@ -25,8 +25,7 @@ import { Spinner, Switch } from "@app/components/v2"; -import { ProjectPermissionSub, useProjectPermission } from "@app/context"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; +import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; import { useDebounce } from "@app/hooks"; import { useMoveSecrets } from "@app/hooks/api"; import { useGetProjectSecretsQuickSearch } from "@app/hooks/api/dashboard"; @@ -96,7 +95,7 @@ const Content = ({ env.slug, { missingPermissions: permission.cannot( - ProjectPermissionSecretActions.Delete, + ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: sourceSecretPath, diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx index 5cbefe9c3c..daf875a78a 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/SecretDashboardPage.tsx @@ -25,7 +25,6 @@ import { useProjectPermission, useWorkspace } from "@app/context"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks"; import { useGetImportedSecretsSingleEnv, @@ -104,16 +103,7 @@ const Page = () => { const projectSlug = currentWorkspace?.slug || ""; const secretPath = (routerQueryParams.secretPath as string) || "/"; const canReadSecret = permission.can( - ProjectPermissionSecretActions.DescribeSecret, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: "*", - secretTags: ["*"] - }) - ); - const canReadSecretValue = permission.can( - ProjectPermissionSecretActions.ReadValue, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -186,7 +176,6 @@ const Page = () => { orderDirection, includeImports: canReadSecretImports && filter.include.import, includeFolders: filter.include.folder, - viewSecretValue: canReadSecretValue, includeDynamicSecrets: canReadDynamicSecret && filter.include.dynamic, includeSecrets: canReadSecret && filter.include.secret, tags: filter.tags diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx index d545ba7483..03ff3dd57c 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx @@ -21,7 +21,6 @@ import { faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { AxiosError } from "axios"; import FileSaver from "file-saver"; import { twMerge } from "tailwind-merge"; @@ -55,7 +54,7 @@ import { import { usePopUp } from "@app/hooks"; import { useCreateFolder, useDeleteSecretBatch, useMoveSecrets } from "@app/hooks/api"; import { fetchProjectSecrets } from "@app/hooks/api/secrets/queries"; -import { ApiErrorTypes, SecretType, TApiErrors, WsTag } from "@app/hooks/api/types"; +import { SecretType, WsTag } from "@app/hooks/api/types"; import { SecretSearchInput } from "@app/pages/secret-manager/OverviewPage/components/SecretSearchInput"; import { @@ -153,71 +152,51 @@ export const ActionBar = ({ }; const handleSecretDownload = async () => { - try { - const { secrets: localSecrets, imports: localImportedSecrets } = await fetchProjectSecrets({ - workspaceId, - expandSecretReferences: true, - includeImports: true, - environment, - secretPath - }); - const secretsPicked = new Set(); - const secretsToDownload: { key: string; value?: string; comment?: string }[] = []; - localSecrets.forEach((el) => { - secretsPicked.add(el.secretKey); - secretsToDownload.push({ - key: el.secretKey, - value: el.secretValue, - comment: el.secretComment - }); + const { secrets: localSecrets, imports: localImportedSecrets } = await fetchProjectSecrets({ + workspaceId, + expandSecretReferences: true, + includeImports: true, + environment, + secretPath + }); + const secretsPicked = new Set(); + const secretsToDownload: { key: string; value?: string; comment?: string }[] = []; + localSecrets.forEach((el) => { + secretsPicked.add(el.secretKey); + secretsToDownload.push({ + key: el.secretKey, + value: el.secretValue, + comment: el.secretComment }); + }); - for (let i = localImportedSecrets.length - 1; i >= 0; i -= 1) { - for (let j = localImportedSecrets[i].secrets.length - 1; j >= 0; j -= 1) { - const secret = localImportedSecrets[i].secrets[j]; - if (!secretsPicked.has(secret.secretKey)) { - secretsToDownload.push({ - key: secret.secretKey, - value: secret.secretValue, - comment: secret.secretComment - }); - } - secretsPicked.add(secret.secretKey); - } - } - - const file = secretsToDownload - .sort((a, b) => a.key.toLowerCase().localeCompare(b.key.toLowerCase())) - .reduce( - (prev, { key, comment, value }, index) => - prev + - (comment - ? `${index === 0 ? "#" : "\n#"} ${comment}\n${key}=${value}\n` - : `${key}=${value}\n`), - "" - ); - - const blob = new Blob([file], { type: "text/plain;charset=utf-8" }); - FileSaver.saveAs(blob, `${environment}.env`); - } catch (err) { - if (err instanceof AxiosError) { - const error = err?.response?.data as TApiErrors; - - if (error?.error === ApiErrorTypes.ForbiddenError && error.message.includes("readValue")) { - createNotification({ - title: "You don't have permission to download secrets", - text: "You don't have permission to view one or more of the secrets in the current folder. Please contact your administrator.", - type: "error" + for (let i = localImportedSecrets.length - 1; i >= 0; i -= 1) { + for (let j = localImportedSecrets[i].secrets.length - 1; j >= 0; j -= 1) { + const secret = localImportedSecrets[i].secrets[j]; + if (!secretsPicked.has(secret.secretKey)) { + secretsToDownload.push({ + key: secret.secretKey, + value: secret.secretValue, + comment: secret.secretComment }); - return; } + secretsPicked.add(secret.secretKey); } - createNotification({ - title: "Failed to download secrets", - text: "Please try again later.", - type: "error" - }); } + + const file = secretsToDownload + .sort((a, b) => a.key.toLowerCase().localeCompare(b.key.toLowerCase())) + .reduce( + (prev, { key, comment, value }, index) => + prev + + (comment + ? `${index === 0 ? "#" : "\n#"} ${comment}\n${key}=${value}\n` + : `${key}=${value}\n`), + "" + ); + + const blob = new Blob([file], { type: "text/plain;charset=utf-8" }); + FileSaver.saveAs(blob, `${environment}.env`); }; const handleSecretBulkDelete = async () => { diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx index cc60b29d02..168cd6f43a 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx @@ -9,14 +9,12 @@ import { faPlus, faShare, faTag, - faTrash, - faTriangleExclamation + faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { Link } from "@tanstack/react-router"; import { format } from "date-fns"; -import { twMerge } from "tailwind-merge"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { createNotification } from "@app/components/notifications"; @@ -46,7 +44,6 @@ import { useProjectPermission, useWorkspace } from "@app/context"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; import { usePopUp, useToggle } from "@app/hooks"; import { useGetSecretVersion } from "@app/hooks/api"; import { useGetSecretAccessList } from "@app/hooks/api/secrets/queries"; @@ -125,7 +122,7 @@ export const SecretDetailSidebar = ({ const selectTagSlugs = selectedTags.map((i) => i.slug); const cannotEditSecret = permission.cannot( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -133,29 +130,16 @@ export const SecretDetailSidebar = ({ secretTags: selectTagSlugs }) ); - - const cannotReadSecretValue = permission.cannot( - ProjectPermissionSecretActions.ReadValue, - subject(ProjectPermissionSub.Secrets, { - environment, - secretPath, - secretName: secretKey, - secretTags: selectTagSlugs - }) - ); - const isReadOnly = permission.can( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath, secretName: secretKey, secretTags: selectTagSlugs }) - ) && - cannotEditSecret && - cannotReadSecretValue; + ) && cannotEditSecret; const overrideAction = watch("overrideAction"); const isOverridden = @@ -208,16 +192,9 @@ export const SecretDetailSidebar = ({ await onSaveSecret(secret, { ...secret, ...data }, () => reset()); }; - const handleReminderSubmit = async ( - reminderRepeatDays: number | null | undefined, - reminderNote: string | null | undefined - ) => { - await onSaveSecret( - secret, - { ...secret, reminderRepeatDays, reminderNote, isReminderEvent: true }, - () => {} - ); - }; + const handleReminderSubmit = async (reminderRepeatDays: number | null | undefined, reminderNote: string | null | undefined) => { + await onSaveSecret(secret, { ...secret, reminderRepeatDays, reminderNote, isReminderEvent: true }, () => { }); + } const [createReminderFormOpen, setCreateReminderFormOpen] = useToggle(false); @@ -236,7 +213,7 @@ export const SecretDetailSidebar = ({ if (data) { setValue("reminderRepeatDays", data.days, { shouldDirty: false }); setValue("reminderNote", data.note, { shouldDirty: false }); - handleReminderSubmit(data.days, data.note); + handleReminderSubmit(data.days, data.note) } }} /> @@ -284,63 +261,38 @@ export const SecretDetailSidebar = ({ key="secret-value" control={control} render={({ field }) => ( -
- - - - The value of this secret is hidden because you do not have the - read secret value permission. - -
- ) : undefined - } - label="Value" - > -
- - - - -
- - + + + )} /> )} +
+ +
@@ -666,34 +618,51 @@ export const SecretDetailSidebar = ({
Version History
- {secretVersion?.map( - ({ createdAt, secretValue, version, id, secretValueHidden }, index) => ( -
-
-
-
-
- v{version} -
+ {secretVersion?.map(({ createdAt, secretValue, version, id }) => ( +
+
+
+
+
+ v{version}
-
{format(new Date(createdAt), "Pp")}
-
-
-
+
{format(new Date(createdAt), "Pp")}
+
+
+
+
+
+
+
+ Value:
-
-
- Value: -
-
-
- - + -
- - {secretValueHidden ? "******" : secretValue?.replace(/./g, "*")} - +
+ + {secretValue?.replace(/./g, "*")} + - -
+ } + }} + > + + +
-
- - setValue("value", secretValue)} - > - - - -
- ) - )} +
+ + setValue("value", secretValue)} + > + + + +
+
+ ))}
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx index 02f7a0aa98..86905f3519 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-nested-ternary */ /* eslint-disable simple-import-sort/imports */ import { ProjectPermissionCan } from "@app/components/permissions"; import { @@ -45,8 +44,6 @@ import { SecretReferenceTree } from "@app/components/secrets/SecretReferenceDetails"; -import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; -import { Blur } from "@app/components/v2/Blur"; import { FontAwesomeSpriteName, formSchema, @@ -102,14 +99,8 @@ export const SecretItem = memo( trigger, formState: { isDirty, isSubmitting, errors } } = useForm({ - defaultValues: { - ...secret, - value: secret.secretValueHidden ? "" : secret.value - }, - values: { - ...secret, - value: secret.secretValueHidden ? "" : secret.value - }, + defaultValues: secret, + values: secret, resolver: zodResolver(formSchema) }); @@ -132,7 +123,7 @@ export const SecretItem = memo( const isReadOnly = permission.can( - ProjectPermissionSecretActions.DescribeSecret, + ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -141,7 +132,7 @@ export const SecretItem = memo( }) ) && permission.cannot( - ProjectPermissionSecretActions.Edit, + ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath, @@ -150,8 +141,6 @@ export const SecretItem = memo( }) ); - const { secretValueHidden } = secret; - const [isSecValueCopied, setIsSecValueCopied] = useToggle(false); useEffect(() => { let timer: NodeJS.Timeout; @@ -283,8 +272,6 @@ export const SecretItem = memo( /> )} /> - ) : secretValueHidden ? ( - ) : ( )} @@ -307,7 +293,6 @@ export const SecretItem = memo(
{ className="w-full px-0 blur-sm placeholder:text-red-500 focus:text-bunker-100 focus:ring-transparent" />
- +
+ ******** +
))} diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SnapshotView/SecretItem.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SnapshotView/SecretItem.tsx index 076cf3b6df..d6680bcef9 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SnapshotView/SecretItem.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SnapshotView/SecretItem.tsx @@ -20,7 +20,6 @@ import { Tooltip, Tr } from "@app/components/v2"; -import { Blur } from "@app/components/v2/Blur"; import { useToggle } from "@app/hooks"; import { SecretV3RawSanitized } from "@app/hooks/api/secrets/types"; @@ -121,25 +120,11 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => { Value {isModified && ( - {preSecret?.secretValueHidden ? ( - - ) : ( - - )} + )} - {postSecret?.secretValueHidden ? ( - - ) : ( - - )} + {Boolean(preSecret?.idOverride || postSecret?.idOverride) && (