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