mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
fix: view secret value permission (requested changes)
This commit is contained in:
@@ -10,7 +10,7 @@ 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 { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||
|
||||
@@ -250,14 +250,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
}
|
||||
});
|
||||
|
||||
const tagSchema = SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.array()
|
||||
.optional();
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:id",
|
||||
@@ -291,7 +283,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
.omit({ _id: true, environment: true, workspace: true, type: true, version: true })
|
||||
.extend({
|
||||
op: z.string(),
|
||||
tags: tagSchema,
|
||||
tags: SanitizedTagSchema.array().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.nullish(),
|
||||
secret: z
|
||||
.object({
|
||||
@@ -310,7 +302,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
secretKey: z.string(),
|
||||
secretValue: z.string().optional(),
|
||||
secretComment: z.string().optional(),
|
||||
tags: tagSchema,
|
||||
tags: SanitizedTagSchema.array().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.nullish()
|
||||
})
|
||||
.optional()
|
||||
|
||||
@@ -31,6 +31,7 @@ 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: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
@@ -55,6 +56,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||
actorOrgId: req.permission.orgId,
|
||||
id: req.params.secretSnapshotId
|
||||
});
|
||||
|
||||
return { secretSnapshot };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -442,7 +442,7 @@ export const ProjectPermissionV1Schema = 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(ProjectPermissionActions).describe(
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionSecretActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
),
|
||||
conditions: SecretConditionV1Schema.describe(
|
||||
@@ -947,7 +947,17 @@ 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
|
||||
@@ -989,6 +999,7 @@ 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
|
||||
);
|
||||
|
||||
@@ -111,7 +111,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "encryptWithInputKey" | "decryptWithInputKey">;
|
||||
secretV2BridgeDAL: Pick<
|
||||
TSecretV2BridgeDALFactory,
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany"
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find"
|
||||
>;
|
||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
|
||||
@@ -265,6 +265,7 @@ export const secretReplicationServiceFactory = ({
|
||||
folderDAL,
|
||||
secretImportDAL,
|
||||
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""),
|
||||
viewSecretValue: true,
|
||||
hasSecretAccess: () => true
|
||||
});
|
||||
// secrets that gets replicated across imports
|
||||
|
||||
@@ -38,6 +38,7 @@ 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;
|
||||
@@ -184,7 +185,7 @@ export const secretSnapshotServiceFactory = ({
|
||||
snapshotDetails = {
|
||||
...encryptedSnapshotDetails,
|
||||
secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
const canReadValue = permission.can(
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: encryptedSnapshotDetails.environment.slug,
|
||||
@@ -194,12 +195,20 @@ export const secretSnapshotServiceFactory = ({
|
||||
})
|
||||
);
|
||||
|
||||
let secretValue = "";
|
||||
if (canReadValue) {
|
||||
secretValue = el.encryptedValue
|
||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
||||
: "";
|
||||
} else {
|
||||
secretValue = INFISICAL_SECRET_VALUE_HIDDEN_MASK;
|
||||
}
|
||||
|
||||
return {
|
||||
...el,
|
||||
secretKey: el.key,
|
||||
secretValue: el.encryptedValue
|
||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
||||
: "",
|
||||
secretValueHidden: !canReadValue,
|
||||
secretValue,
|
||||
secretComment: el.encryptedComment
|
||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
|
||||
: ""
|
||||
@@ -228,7 +237,7 @@ export const secretSnapshotServiceFactory = ({
|
||||
key: botKey
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
const canReadValue = permission.can(
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: encryptedSnapshotDetails.environment.slug,
|
||||
@@ -238,15 +247,24 @@ export const secretSnapshotServiceFactory = ({
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...el,
|
||||
secretKey,
|
||||
secretValue: decryptSymmetric128BitHexKeyUTF8({
|
||||
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({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SshCertificatesSchema } from "@app/db/schemas";
|
||||
|
||||
export const sanitizedSshCertificate = SshCertificatesSchema.pick({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ProjectRolesSchema,
|
||||
ProjectsSchema,
|
||||
SecretApprovalPoliciesSchema,
|
||||
SecretTagsSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
@@ -232,3 +233,11 @@ export const SanitizedProjectSchema = ProjectsSchema.pick({
|
||||
kmsCertificateKeyId: true,
|
||||
auditLogsRetentionDays: true
|
||||
});
|
||||
|
||||
export const SanitizedTagSchema = SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
}).extend({
|
||||
name: z.string()
|
||||
});
|
||||
|
||||
@@ -15,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, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||
import { SanitizedDynamicSecretSchema, SanitizedTagSchema, 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";
|
||||
@@ -119,14 +119,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secretValueHidden: z.boolean(),
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional()
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
@@ -416,14 +409,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secretValueHidden: z.boolean(),
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional()
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
@@ -605,24 +591,25 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
if (remainingLimit > 0 && totalSecretCount > adjustedOffset) {
|
||||
const secretsRaw = await server.services.secret.getSecretsRaw({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
viewSecretValue: req.query.viewSecretValue,
|
||||
actorOrgId: req.permission.orgId,
|
||||
environment,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
projectId,
|
||||
path: secretPath,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
search,
|
||||
limit: remainingLimit,
|
||||
offset: adjustedOffset,
|
||||
tagSlugs: tags
|
||||
});
|
||||
|
||||
secrets = secretsRaw.secrets;
|
||||
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;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
@@ -703,14 +690,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
.extend({
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional()
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
@@ -868,7 +848,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
environment: z.string().trim(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
keys: z.string().trim().transform(decodeURIComponent),
|
||||
viewSecretValue: booleanSchema.default(true)
|
||||
viewSecretValue: booleanSchema.default(false)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -877,14 +857,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secretValueHidden: z.boolean(),
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional()
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
|
||||
@@ -23,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 { secretRawSchema } from "../sanitizedSchemas";
|
||||
import { SanitizedTagSchema, secretRawSchema } from "../sanitizedSchemas";
|
||||
|
||||
const SecretReferenceNode = z.object({
|
||||
key: z.string(),
|
||||
@@ -72,7 +72,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
body: z.object({
|
||||
projectSlug: z.string().trim().describe(SECRETS.ATTACH_TAGS.projectSlug),
|
||||
environment: z.string().trim().describe(SECRETS.ATTACH_TAGS.environment),
|
||||
viewSecretValue: convertStringBoolean(true).describe(SECRETS.ATTACH_TAGS.viewSecretValue),
|
||||
secretPath: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -84,17 +83,9 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
secret: SecretsSchema.omit({ secretBlindIndex: true }).merge(
|
||||
z.object({
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
})
|
||||
)
|
||||
secret: SecretsSchema.omit({ secretBlindIndex: true }).extend({
|
||||
tags: SanitizedTagSchema.array()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -148,13 +139,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
response: {
|
||||
200: z.object({
|
||||
secret: SecretsSchema.omit({ secretBlindIndex: true }).extend({
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
tags: SanitizedTagSchema.array()
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -274,14 +259,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secretPath: z.string().optional(),
|
||||
secretValueHidden: z.boolean(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional()
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array(),
|
||||
imports: z
|
||||
@@ -292,8 +270,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: secretRawSchema
|
||||
.omit({ createdAt: true, updatedAt: true })
|
||||
.extend({
|
||||
// No `secretValueHidden` on imports, because imported secrets that the user doesn't have read value permission on, will be filtered out.
|
||||
// Therefore all returned secret imports will have the value present.
|
||||
secretValueHidden: z.boolean(),
|
||||
secretMetadata: ResourceMetadataSchema.optional()
|
||||
})
|
||||
.array()
|
||||
@@ -414,14 +391,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
200: z.object({
|
||||
secret: secretRawSchema.extend({
|
||||
secretValueHidden: z.boolean(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
.optional(),
|
||||
tags: SanitizedTagSchema.array().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional()
|
||||
})
|
||||
})
|
||||
@@ -845,13 +815,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
workspace: z.string(),
|
||||
environment: z.string(),
|
||||
secretPath: z.string().optional(),
|
||||
tags: SecretTagsSchema.pick({
|
||||
id: true,
|
||||
slug: true,
|
||||
color: true
|
||||
})
|
||||
.extend({ name: z.string() })
|
||||
.array()
|
||||
tags: SanitizedTagSchema.array()
|
||||
})
|
||||
.array(),
|
||||
imports: z
|
||||
@@ -945,7 +909,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
workspaceId: z.string().trim(),
|
||||
environment: z.string().trim(),
|
||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||
viewSecretValue: convertStringBoolean(true),
|
||||
type: z.nativeEnum(SecretType).default(SecretType.Shared),
|
||||
version: z.coerce.number().optional(),
|
||||
include_imports: convertStringBoolean()
|
||||
|
||||
@@ -31,7 +31,7 @@ export type TImportDataIntoInfisicalDTO = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findLastEnvPosition" | "create" | "findOne">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "find">;
|
||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "create">;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create" | "find">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||
|
||||
@@ -27,7 +27,7 @@ export type TExternalMigrationQueueFactoryDep = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findLastEnvPosition" | "create" | "findOne">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "find">;
|
||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "create">;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create" | "find">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||
|
||||
@@ -68,7 +68,8 @@ const getIntegrationSecretsV2 = async (
|
||||
secretDAL: secretV2BridgeDAL,
|
||||
secretImportDAL,
|
||||
secretImports,
|
||||
hasSecretAccess: () => true
|
||||
hasSecretAccess: () => true,
|
||||
viewSecretValue: true
|
||||
});
|
||||
|
||||
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { TSecretDALFactory } from "../secret/secret-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||
import { TSecretImportDALFactory } from "./secret-import-dal";
|
||||
import { INFISICAL_SECRET_VALUE_HIDDEN_MASK } from "../secret/secret-fns";
|
||||
|
||||
type TSecretImportSecrets = {
|
||||
secretPath: string;
|
||||
@@ -39,6 +40,7 @@ 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;
|
||||
})[];
|
||||
@@ -150,12 +152,14 @@ export const fnSecretsV2FromImports = async ({
|
||||
secretImportDAL,
|
||||
decryptor,
|
||||
expandSecretReferences,
|
||||
hasSecretAccess
|
||||
hasSecretAccess,
|
||||
viewSecretValue
|
||||
}: {
|
||||
secretImports: (Omit<TSecretImports, "importEnv"> & {
|
||||
importEnv: { id: string; slug: string; name: string };
|
||||
})[];
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findByManySecretPath">;
|
||||
viewSecretValue: boolean;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "find">;
|
||||
secretImportDAL: Pick<TSecretImportDALFactory, "findByFolderIds">;
|
||||
decryptor: (value?: Buffer | null) => string;
|
||||
@@ -168,9 +172,11 @@ 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[] }[] = [
|
||||
{ secretImports: rootSecretImports, depth: 0, parentImportedSecrets: [] }
|
||||
];
|
||||
const stack: {
|
||||
secretImports: typeof rootSecretImports;
|
||||
depth: number;
|
||||
parentImportedSecrets: (TSecretsV2 & { secretValueHidden: boolean })[];
|
||||
}[] = [{ secretImports: rootSecretImports, depth: 0, parentImportedSecrets: [] }];
|
||||
|
||||
const processedImports: TSecretImportSecretsV2[] = [];
|
||||
|
||||
@@ -229,7 +235,8 @@ export const fnSecretsV2FromImports = async ({
|
||||
.map((item) => ({
|
||||
...item,
|
||||
secretKey: item.key,
|
||||
secretValue: decryptor(item.encryptedValue),
|
||||
secretValue: viewSecretValue ? decryptor(item.encryptedValue) : INFISICAL_SECRET_VALUE_HIDDEN_MASK,
|
||||
secretValueHidden: !viewSecretValue,
|
||||
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.
|
||||
@@ -267,6 +274,8 @@ 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,
|
||||
|
||||
@@ -94,7 +94,7 @@ export const secretImportServiceFactory = ({
|
||||
|
||||
// check if user has permission to import from target path
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
ProjectPermissionSecretActions.DescribeSecret,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: data.environment,
|
||||
secretPath: data.path
|
||||
@@ -406,7 +406,7 @@ export const secretImportServiceFactory = ({
|
||||
|
||||
// check if user has permission to import from target path
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
ProjectPermissionSecretActions.DescribeSecret,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: secretImportDoc.importEnv.slug,
|
||||
secretPath: secretImportDoc.importPath
|
||||
@@ -646,6 +646,7 @@ export const secretImportServiceFactory = ({
|
||||
const importedSecrets = await fnSecretsV2FromImports({
|
||||
secretImports,
|
||||
folderDAL,
|
||||
viewSecretValue: true,
|
||||
secretDAL: secretV2BridgeDAL,
|
||||
secretImportDAL,
|
||||
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : ""),
|
||||
|
||||
@@ -249,7 +249,8 @@ export const secretSyncQueueFactory = ({
|
||||
expandSecretReferences,
|
||||
secretImportDAL,
|
||||
secretImports,
|
||||
hasSecretAccess: () => true
|
||||
hasSecretAccess: () => true,
|
||||
viewSecretValue: true
|
||||
});
|
||||
|
||||
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
|
||||
|
||||
@@ -148,7 +148,16 @@ export const fnSecretBulkInsert = async ({
|
||||
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
|
||||
}
|
||||
|
||||
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||
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 }));
|
||||
};
|
||||
|
||||
export const fnSecretBulkUpdate = async ({
|
||||
@@ -286,7 +295,15 @@ export const fnSecretBulkUpdate = async ({
|
||||
tx
|
||||
);
|
||||
|
||||
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||
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 }));
|
||||
};
|
||||
|
||||
export const fnSecretBulkDelete = async ({
|
||||
@@ -627,7 +644,7 @@ export const reshapeBridgeSecret = (
|
||||
}[];
|
||||
secretMetadata?: ResourceMetadataDTO;
|
||||
},
|
||||
secretValueHidden?: boolean
|
||||
secretValueHidden: boolean
|
||||
) => ({
|
||||
secretKey: secret.key,
|
||||
secretPath,
|
||||
|
||||
@@ -323,11 +323,17 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
return reshapeBridgeSecret(projectId, environment, secretPath, {
|
||||
...secret,
|
||||
value: inputSecret.secretValue,
|
||||
comment: inputSecret.secretComment || ""
|
||||
});
|
||||
return reshapeBridgeSecret(
|
||||
projectId,
|
||||
environment,
|
||||
secretPath,
|
||||
{
|
||||
...secret,
|
||||
value: inputSecret.secretValue,
|
||||
comment: inputSecret.secretComment || ""
|
||||
},
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
const updateSecret = async ({
|
||||
@@ -407,9 +413,9 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
// validate tags
|
||||
// fetch all tags and if not same count throw error meaning one was invalid tags
|
||||
const tags = inputSecret.tagIds ? await secretTagDAL.find({ projectId, $in: { id: inputSecret.tagIds } }) : [];
|
||||
if ((inputSecret.tagIds || []).length !== tags.length)
|
||||
throw new NotFoundError({ message: `Tag not found. Found ${tags.map((el) => el.slug).join(",")}` });
|
||||
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(",")}` });
|
||||
|
||||
// now check with new ids
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
@@ -418,7 +424,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
environment,
|
||||
secretPath,
|
||||
secretName: inputSecret.secretName,
|
||||
secretTags: tags?.map((el) => el.slug)
|
||||
secretTags: newTags?.map((el) => el.slug)
|
||||
})
|
||||
);
|
||||
|
||||
@@ -435,7 +441,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
environment,
|
||||
secretPath,
|
||||
secretName: inputSecret.newSecretName,
|
||||
secretTags: tags?.map((el) => el.slug)
|
||||
secretTags: newTags?.map((el) => el.slug)
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -513,13 +519,14 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const tagsToCheck = newTags?.length ? newTags : secret.tags;
|
||||
const secretValueHidden = !permission.can(
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment,
|
||||
secretPath,
|
||||
secretName: inputSecret.secretName, // ! Note(Daniel): We are checking for the EXISTING secret name, not the new secret name.
|
||||
secretTags: tags?.map((el) => el.slug) // ! Note(Daniel): Same here
|
||||
secretName: inputSecret.secretName,
|
||||
secretTags: tagsToCheck.length ? tagsToCheck.map((el) => el.slug) : undefined
|
||||
})
|
||||
);
|
||||
|
||||
@@ -738,7 +745,13 @@ export const secretV2BridgeServiceFactory = ({
|
||||
};
|
||||
|
||||
const getSecretsByFolderMappings = async (
|
||||
{ projectId, userId, filters, folderMappings, filterByAction }: TGetSecretsRawByFolderMappingsDTO,
|
||||
{
|
||||
projectId,
|
||||
userId,
|
||||
filters,
|
||||
folderMappings,
|
||||
filterByAction = ProjectPermissionSecretActions.ReadValue
|
||||
}: TGetSecretsRawByFolderMappingsDTO,
|
||||
projectPermission: Awaited<ReturnType<typeof permissionService.getProjectPermission>>["permission"]
|
||||
) => {
|
||||
const groupedFolderMappings = groupBy(folderMappings, (folderMapping) => folderMapping.folderId);
|
||||
@@ -755,13 +768,10 @@ export const secretV2BridgeServiceFactory = ({
|
||||
projectId
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
if (!filterByAction) filterByAction = ProjectPermissionSecretActions.ReadValue;
|
||||
|
||||
const decryptedSecrets = secrets
|
||||
.filter((el) =>
|
||||
projectPermission.can(
|
||||
filterByAction as ProjectPermissionSecretActions, // ? Typescript assumes that filterByAction may be undefined, which is not true, so we are casting it to ProjectPermissionSecretActions
|
||||
filterByAction,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment: groupedFolderMappings[el.folderId][0].environment,
|
||||
secretPath: groupedFolderMappings[el.folderId][0].path,
|
||||
@@ -870,6 +880,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
includeImports,
|
||||
recursive,
|
||||
expandSecretReferences: shouldExpandSecretReferences,
|
||||
throwOnMissingReadValuePermission = true,
|
||||
...params
|
||||
}: TGetSecretsDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
@@ -927,19 +938,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
const decryptedSecrets = secrets
|
||||
.filter((el) => {
|
||||
if (viewSecretValue) {
|
||||
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)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return permission.can(
|
||||
const canDescribeSecret = permission.can(
|
||||
ProjectPermissionSecretActions.DescribeSecret,
|
||||
subject(ProjectPermissionSub.Secrets, {
|
||||
environment,
|
||||
@@ -948,6 +947,43 @@ export const secretV2BridgeServiceFactory = ({
|
||||
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)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return canDescribeSecret;
|
||||
})
|
||||
.map((secret) => {
|
||||
const isPersonalSecret = secret.userId === actorId && secret.type === SecretType.Personal;
|
||||
@@ -1027,6 +1063,7 @@ 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,
|
||||
@@ -1174,6 +1211,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
const secretImports = await secretImportDAL.find({ folderId, isReplication: false });
|
||||
const importedSecrets = await fnSecretsV2FromImports({
|
||||
secretImports,
|
||||
viewSecretValue,
|
||||
secretDAL,
|
||||
folderDAL,
|
||||
secretImportDAL,
|
||||
@@ -1207,11 +1245,17 @@ export const secretV2BridgeServiceFactory = ({
|
||||
for (let j = 0; j < importedSecrets[i].secrets.length; j += 1) {
|
||||
const importedSecret = importedSecrets[i].secrets[j];
|
||||
if (secretName === importedSecret.key) {
|
||||
return reshapeBridgeSecret(projectId, importedSecrets[i].environment, importedSecrets[i].secretPath, {
|
||||
...importedSecret,
|
||||
value: importedSecret.secretValue || "",
|
||||
comment: importedSecret.secretComment || ""
|
||||
});
|
||||
return reshapeBridgeSecret(
|
||||
projectId,
|
||||
importedSecrets[i].environment,
|
||||
importedSecrets[i].secretPath,
|
||||
{
|
||||
...importedSecret,
|
||||
value: importedSecret.secretValue || "",
|
||||
comment: importedSecret.secretComment || ""
|
||||
},
|
||||
importedSecret.secretValueHidden
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1365,8 +1409,8 @@ export const secretV2BridgeServiceFactory = ({
|
||||
const { encryptor: secretManagerEncryptor, decryptor: secretManagerDecryptor } =
|
||||
await kmsService.createCipherPairWithDataKey({ type: KmsDataKey.SecretManager, projectId });
|
||||
|
||||
const newSecrets = await secretDAL.transaction(async (tx) => {
|
||||
const createdSecrets = await fnSecretBulkInsert({
|
||||
const newSecrets = await secretDAL.transaction(async (tx) =>
|
||||
fnSecretBulkInsert({
|
||||
inputSecrets: inputSecrets.map((el) => {
|
||||
const references = secretReferencesGroupByInputSecretKey[el.secretKey]?.nestedReferences;
|
||||
|
||||
@@ -1395,19 +1439,8 @@ export const secretV2BridgeServiceFactory = ({
|
||||
secretTagDAL,
|
||||
secretVersionTagDAL,
|
||||
tx
|
||||
});
|
||||
|
||||
const secs = await secretDAL.find(
|
||||
{
|
||||
$in: {
|
||||
[`${TableName.SecretV2}.id` as "id"]: createdSecrets.map((el) => el.id)
|
||||
}
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
|
||||
return secs;
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await snapshotService.performSnapshot(folderId);
|
||||
await secretQueueService.syncSecrets({
|
||||
@@ -1656,7 +1689,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
});
|
||||
await $validateSecretReferences(projectId, permission, secretReferences, tx);
|
||||
|
||||
const bulkUpdatedSecretsRes = await fnSecretBulkUpdate({
|
||||
const bulkUpdatedSecrets = await fnSecretBulkUpdate({
|
||||
folderId,
|
||||
orgId: actorOrgId,
|
||||
tx,
|
||||
@@ -1694,20 +1727,9 @@ export const secretV2BridgeServiceFactory = ({
|
||||
resourceMetadataDAL
|
||||
});
|
||||
|
||||
const bulkUpdatedSecrets = await secretDAL.find(
|
||||
{
|
||||
$in: {
|
||||
[`${TableName.SecretV2}.id` as "id"]: bulkUpdatedSecretsRes.map((el) => el.id)
|
||||
}
|
||||
},
|
||||
{
|
||||
tx
|
||||
}
|
||||
);
|
||||
|
||||
updatedSecrets.push(...bulkUpdatedSecrets.map((el) => ({ ...el, secretPath: folder.path })));
|
||||
if (updateMode === SecretUpdateMode.Upsert) {
|
||||
const bulkInsertedSecretsRes = await fnSecretBulkInsert({
|
||||
const bulkInsertedSecrets = await fnSecretBulkInsert({
|
||||
inputSecrets: secretsToCreate.map((el) => {
|
||||
const references = secretReferencesGroupByInputSecretKey[el.secretKey]?.nestedReferences;
|
||||
|
||||
@@ -1738,15 +1760,6 @@ export const secretV2BridgeServiceFactory = ({
|
||||
tx
|
||||
});
|
||||
|
||||
const bulkInsertedSecrets = await secretDAL.find(
|
||||
{
|
||||
$in: {
|
||||
[`${TableName.SecretV2}.id` as "id"]: bulkInsertedSecretsRes.map((el) => el.id)
|
||||
}
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
|
||||
updatedSecrets.push(...bulkInsertedSecrets.map((el) => ({ ...el, secretPath: folder.path })));
|
||||
}
|
||||
}
|
||||
@@ -1957,11 +1970,17 @@ export const secretV2BridgeServiceFactory = ({
|
||||
|
||||
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() : ""
|
||||
})
|
||||
reshapeBridgeSecret(
|
||||
folder.projectId,
|
||||
folder.environment.envSlug,
|
||||
"/",
|
||||
{
|
||||
...el,
|
||||
value: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
|
||||
comment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() : ""
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export type TGetSecretsDTO = {
|
||||
recursive?: boolean;
|
||||
tagSlugs?: string[];
|
||||
viewSecretValue: boolean;
|
||||
throwOnMissingReadValuePermission?: boolean;
|
||||
metadataFilter?: {
|
||||
key?: string;
|
||||
value?: string;
|
||||
@@ -50,6 +51,11 @@ export type TGetSecretsDTO = {
|
||||
keys?: string[];
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TGetSecretsMissingReadValuePermissionDTO = Omit<
|
||||
TGetSecretsDTO,
|
||||
"viewSecretValue" | "recursive" | "expandSecretReferences"
|
||||
>;
|
||||
|
||||
export type TGetASecretDTO = {
|
||||
secretName: string;
|
||||
path: string;
|
||||
@@ -167,7 +173,7 @@ export type TFnSecretBulkInsert = {
|
||||
}
|
||||
>;
|
||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences" | "find">;
|
||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "find">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
@@ -191,7 +197,7 @@ export type TFnSecretBulkUpdate = {
|
||||
data: TRequireReferenceIfValue & { tags?: string[]; secretMetadata?: ResourceMetadataDTO };
|
||||
}[];
|
||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
|
||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences" | "find">;
|
||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2" | "find">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
|
||||
@@ -402,7 +402,8 @@ export const secretQueueFactory = ({
|
||||
expandSecretReferences,
|
||||
secretImportDAL,
|
||||
secretImports,
|
||||
hasSecretAccess: () => true
|
||||
hasSecretAccess: () => true,
|
||||
viewSecretValue: true
|
||||
});
|
||||
|
||||
for (let i = importedSecrets.length - 1; i >= 0; i -= 1) {
|
||||
|
||||
@@ -667,7 +667,15 @@ export const secretServiceFactory = ({
|
||||
environment,
|
||||
secretPath: groupedPaths[secret.folderId][0].path
|
||||
})),
|
||||
imports: importedSecrets
|
||||
imports: importedSecrets.map((el) => {
|
||||
return {
|
||||
...el,
|
||||
secrets: el.secrets.map((secret) => ({
|
||||
...secret,
|
||||
secretValueHidden: false
|
||||
}))
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -974,7 +982,7 @@ export const secretServiceFactory = ({
|
||||
});
|
||||
|
||||
const secretValueHidden = !permission.can(
|
||||
ProjectPermissionSecretActions.Edit,
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })
|
||||
);
|
||||
|
||||
@@ -1299,6 +1307,7 @@ export const secretServiceFactory = ({
|
||||
expandSecretReferences,
|
||||
recursive,
|
||||
tagSlugs = [],
|
||||
throwOnMissingReadValuePermission = true,
|
||||
...paramsV2
|
||||
}: TGetSecretsRawDTO) => {
|
||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||
@@ -1310,6 +1319,7 @@ export const secretServiceFactory = ({
|
||||
actor,
|
||||
actorOrgId,
|
||||
viewSecretValue,
|
||||
throwOnMissingReadValuePermission,
|
||||
environment,
|
||||
path,
|
||||
recursive,
|
||||
@@ -1318,6 +1328,7 @@ export const secretServiceFactory = ({
|
||||
tagSlugs,
|
||||
...paramsV2
|
||||
});
|
||||
|
||||
return { secrets, imports };
|
||||
}
|
||||
|
||||
@@ -1370,6 +1381,7 @@ export const secretServiceFactory = ({
|
||||
const importedEntries = decryptedImportSecrets.reduce(
|
||||
(
|
||||
accum: {
|
||||
secretValueHidden: boolean;
|
||||
secretKey: string;
|
||||
secretPath: string;
|
||||
workspace: string;
|
||||
@@ -1413,6 +1425,7 @@ 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,
|
||||
@@ -1429,6 +1442,7 @@ 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,
|
||||
|
||||
@@ -181,6 +181,7 @@ export type TGetSecretsRawDTO = {
|
||||
path: string;
|
||||
environment: string;
|
||||
viewSecretValue: boolean;
|
||||
throwOnMissingReadValuePermission?: boolean;
|
||||
includeImports?: boolean;
|
||||
recursive?: boolean;
|
||||
tagSlugs?: string[];
|
||||
@@ -411,7 +412,7 @@ export type TCreateManySecretsRawFnFactory = {
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
secretV2BridgeDAL: Pick<
|
||||
TSecretV2BridgeDALFactory,
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany"
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find"
|
||||
>;
|
||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
@@ -448,7 +449,7 @@ export type TUpdateManySecretsRawFnFactory = {
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
secretV2BridgeDAL: Pick<
|
||||
TSecretV2BridgeDALFactory,
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany"
|
||||
"insertMany" | "upsertSecretReferences" | "findBySecretKeys" | "bulkUpdate" | "deleteMany" | "find"
|
||||
>;
|
||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { forwardRef, TextareaHTMLAttributes, useCallback, useMemo, useRef, useSt
|
||||
import { faCircle, faFolder, faKey } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { useDebounce, useToggle } from "@app/hooks";
|
||||
@@ -283,7 +282,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
setIsFocused.off();
|
||||
}}
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
containerClassName={twMerge(containerClassName)}
|
||||
containerClassName={containerClassName}
|
||||
/>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content
|
||||
|
||||
@@ -144,11 +144,7 @@ export const SecretEditRow = ({
|
||||
<div className="flex-grow border-r border-r-mineshaft-600 pl-1 pr-2">
|
||||
{secretValueHidden ? (
|
||||
<Tooltip content="You do not have permission to read the value of this secret.">
|
||||
<div
|
||||
className="flex w-80 flex-grow items-center py-1 pl-4 pr-2"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
>
|
||||
<div className="flex w-80 flex-grow items-center py-1 pl-4 pr-2">
|
||||
<span className="blur">********</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
Reference in New Issue
Block a user