mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 09:27:50 +00:00
feat: get secret by ID
This commit is contained in:
@@ -1073,6 +1073,7 @@ export const registerRoutes = async (
|
|||||||
const secretService = secretServiceFactory({
|
const secretService = secretServiceFactory({
|
||||||
folderDAL,
|
folderDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
|
secretV2BridgeDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
secretBlindIndexDAL,
|
secretBlindIndexDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
|
|||||||
@@ -380,6 +380,48 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/raw/id/:secretId",
|
||||||
|
config: {
|
||||||
|
rateLimit: secretsLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
secret: secretRawSchema.extend({
|
||||||
|
secretPath: z.string(),
|
||||||
|
tags: SecretTagsSchema.pick({
|
||||||
|
id: true,
|
||||||
|
slug: true,
|
||||||
|
color: true
|
||||||
|
})
|
||||||
|
.extend({ name: z.string() })
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { secretId } = req.params;
|
||||||
|
const secret = await server.services.secret.getSecretByIdRaw({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
secretId
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secret };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/raw/:secretName",
|
url: "/raw/:secretName",
|
||||||
|
|||||||
@@ -613,6 +613,9 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.leftJoin(TableName.SecretFolder, `${TableName.SecretV2}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
|
.leftJoin(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
@@ -622,12 +625,13 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
|
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
|
||||||
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
|
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
|
||||||
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
|
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
|
||||||
);
|
)
|
||||||
|
.select(db.ref("projectId").withSchema(TableName.Environment).as("projectId"));
|
||||||
|
|
||||||
const docs = sqlNestRelationships({
|
const docs = sqlNestRelationships({
|
||||||
data: rawDocs,
|
data: rawDocs,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (el) => ({ _id: el.id, ...SecretsV2Schema.parse(el) }),
|
parentMapper: (el) => ({ _id: el.id, projectId: el.projectId, ...SecretsV2Schema.parse(el) }),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "tagId",
|
key: "tagId",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { KmsDataKey } from "../kms/kms-types";
|
|||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
import { TSecretQueueFactory } from "../secret/secret-queue";
|
import { TSecretQueueFactory } from "../secret/secret-queue";
|
||||||
|
import { TGetASecretByIdDTO } from "../secret/secret-types";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
||||||
@@ -73,7 +74,13 @@ type TSecretV2BridgeServiceFactoryDep = {
|
|||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "findBySlugs">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "findBySlugs">;
|
||||||
folderDAL: Pick<
|
folderDAL: Pick<
|
||||||
TSecretFolderDALFactory,
|
TSecretFolderDALFactory,
|
||||||
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" | "findBySecretPathMultiEnv"
|
| "findBySecretPath"
|
||||||
|
| "updateById"
|
||||||
|
| "findById"
|
||||||
|
| "findByManySecretPath"
|
||||||
|
| "find"
|
||||||
|
| "findBySecretPathMultiEnv"
|
||||||
|
| "findSecretPathByFolderIds"
|
||||||
>;
|
>;
|
||||||
secretImportDAL: Pick<TSecretImportDALFactory, "find" | "findByFolderIds">;
|
secretImportDAL: Pick<TSecretImportDALFactory, "find" | "findByFolderIds">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "handleSecretReminder" | "removeSecretReminder">;
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "handleSecretReminder" | "removeSecretReminder">;
|
||||||
@@ -955,6 +962,70 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSecretById = async ({ actorId, actor, actorOrgId, actorAuthMethod, secret }: TGetASecretByIdDTO) => {
|
||||||
|
const folder = await folderDAL.findById(secret.folderId);
|
||||||
|
if (!folder) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Folder with id '${secret.folderId}' not found`,
|
||||||
|
name: "GetSecretById"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(folder.projectId, [folder.id]);
|
||||||
|
|
||||||
|
if (!folderWithPath) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Folder with id '${folder.id}' not found`,
|
||||||
|
name: "GetSecretById"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId: folder.projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.Secrets, {
|
||||||
|
environment: folder.environment.envSlug,
|
||||||
|
secretPath: folderWithPath.path,
|
||||||
|
secretName: secret.key,
|
||||||
|
secretTags: secret.tags.map((i) => i.slug)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (secret.type === SecretType.Personal && secret.userId !== actorId) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "You are not allowed to access this secret",
|
||||||
|
name: "GetSecretById"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId: folder.projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
const secretValue = secret.encryptedValue
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const secretComment = secret.encryptedComment
|
||||||
|
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedComment }).toString()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return reshapeBridgeSecret(folder.projectId, folder.environment.envSlug, folderWithPath.path, {
|
||||||
|
...secret,
|
||||||
|
value: secretValue,
|
||||||
|
comment: secretComment
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getSecretByName = async ({
|
const getSecretByName = async ({
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
@@ -2237,6 +2308,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
getSecretsCountMultiEnv,
|
getSecretsCountMultiEnv,
|
||||||
getSecretsMultiEnv,
|
getSecretsMultiEnv,
|
||||||
getSecretReferenceTree,
|
getSecretReferenceTree,
|
||||||
getSecretsByFolderMappings
|
getSecretsByFolderMappings,
|
||||||
|
getSecretById
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import {
|
|||||||
SecretEncryptionAlgo,
|
SecretEncryptionAlgo,
|
||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
SecretsSchema,
|
SecretsSchema,
|
||||||
SecretType
|
SecretType,
|
||||||
|
TableName
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
@@ -44,6 +45,7 @@ import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
|||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
import { fnSecretsFromImports } from "../secret-import/secret-import-fns";
|
import { fnSecretsFromImports } from "../secret-import/secret-import-fns";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
import { TSecretV2BridgeServiceFactory } from "../secret-v2-bridge/secret-v2-bridge-service";
|
||||||
import { TGetSecretReferencesTreeDTO } from "../secret-v2-bridge/secret-v2-bridge-types";
|
import { TGetSecretReferencesTreeDTO } from "../secret-v2-bridge/secret-v2-bridge-types";
|
||||||
import { TSecretDALFactory } from "./secret-dal";
|
import { TSecretDALFactory } from "./secret-dal";
|
||||||
@@ -71,6 +73,7 @@ import {
|
|||||||
TDeleteManySecretRawDTO,
|
TDeleteManySecretRawDTO,
|
||||||
TDeleteSecretDTO,
|
TDeleteSecretDTO,
|
||||||
TDeleteSecretRawDTO,
|
TDeleteSecretRawDTO,
|
||||||
|
TGetASecretByIdRawDTO,
|
||||||
TGetASecretDTO,
|
TGetASecretDTO,
|
||||||
TGetASecretRawDTO,
|
TGetASecretRawDTO,
|
||||||
TGetSecretAccessListDTO,
|
TGetSecretAccessListDTO,
|
||||||
@@ -89,13 +92,14 @@ import { TSecretVersionTagDALFactory } from "./secret-version-tag-dal";
|
|||||||
|
|
||||||
type TSecretServiceFactoryDep = {
|
type TSecretServiceFactoryDep = {
|
||||||
secretDAL: TSecretDALFactory;
|
secretDAL: TSecretDALFactory;
|
||||||
|
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "findOneWithTags">;
|
||||||
secretTagDAL: TSecretTagDALFactory;
|
secretTagDAL: TSecretTagDALFactory;
|
||||||
secretVersionDAL: TSecretVersionDALFactory;
|
secretVersionDAL: TSecretVersionDALFactory;
|
||||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug" | "findById">;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
folderDAL: Pick<
|
folderDAL: Pick<
|
||||||
TSecretFolderDALFactory,
|
TSecretFolderDALFactory,
|
||||||
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find"
|
"findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" | "find" | "findSecretPathByFolderIds"
|
||||||
>;
|
>;
|
||||||
secretV2BridgeService: TSecretV2BridgeServiceFactory;
|
secretV2BridgeService: TSecretV2BridgeServiceFactory;
|
||||||
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
secretBlindIndexDAL: TSecretBlindIndexDALFactory;
|
||||||
@@ -124,6 +128,7 @@ type TSecretServiceFactoryDep = {
|
|||||||
export type TSecretServiceFactory = ReturnType<typeof secretServiceFactory>;
|
export type TSecretServiceFactory = ReturnType<typeof secretServiceFactory>;
|
||||||
export const secretServiceFactory = ({
|
export const secretServiceFactory = ({
|
||||||
secretDAL,
|
secretDAL,
|
||||||
|
secretV2BridgeDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
@@ -1382,6 +1387,36 @@ export const secretServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSecretByIdRaw = async ({ secretId, actorId, actor, actorOrgId, actorAuthMethod }: TGetASecretByIdRawDTO) => {
|
||||||
|
const sec = await secretV2BridgeDAL.findOneWithTags({
|
||||||
|
[`${TableName.SecretV2}.id` as "id"]: secretId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!sec) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Secret with id '${secretId}' not found`,
|
||||||
|
name: "GetSecretById"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(sec.projectId);
|
||||||
|
|
||||||
|
if (shouldUseSecretV2Bridge) {
|
||||||
|
const secret = await secretV2BridgeService.getSecretById({
|
||||||
|
secret: sec,
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Project version not supported. Please upgrade your project."
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getSecretByNameRaw = async ({
|
const getSecretByNameRaw = async ({
|
||||||
type,
|
type,
|
||||||
path,
|
path,
|
||||||
@@ -3088,6 +3123,7 @@ export const secretServiceFactory = ({
|
|||||||
getSecretsRawMultiEnv,
|
getSecretsRawMultiEnv,
|
||||||
getSecretReferenceTree,
|
getSecretReferenceTree,
|
||||||
getSecretsRawByFolderMappings,
|
getSecretsRawByFolderMappings,
|
||||||
getSecretAccessList
|
getSecretAccessList,
|
||||||
|
getSecretByIdRaw
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretType, TSecretBlindIndexes, TSecrets, TSecretsInsert, TSecretsUpdate } from "@app/db/schemas";
|
import { SecretType, TSecretBlindIndexes, TSecrets, TSecretsInsert, TSecretsUpdate, TSecretsV2 } from "@app/db/schemas";
|
||||||
import { OrderByDirection, TProjectPermission } from "@app/lib/types";
|
import { OrderByDirection, TProjectPermission } from "@app/lib/types";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
@@ -121,6 +121,17 @@ export type TGetASecretDTO = {
|
|||||||
version?: number;
|
version?: number;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
|
export type TGetASecretByIdDTO = {
|
||||||
|
secret: TSecretsV2 & {
|
||||||
|
tags: {
|
||||||
|
id: string;
|
||||||
|
color?: string | null;
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TCreateBulkSecretDTO = {
|
export type TCreateBulkSecretDTO = {
|
||||||
path: string;
|
path: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@@ -213,6 +224,10 @@ export type TGetASecretRawDTO = {
|
|||||||
projectId?: string;
|
projectId?: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetASecretByIdRawDTO = {
|
||||||
|
secretId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TCreateSecretRawDTO = TProjectPermission & {
|
export type TCreateSecretRawDTO = TProjectPermission & {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user