From e8fd693da37c3c28dbeaa0e9f12e905c98fcfa38 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 18 Jan 2024 20:44:46 +0530 Subject: [PATCH] feat(infisical-pg): added single scope service token auto filling for get secret by name raw and version option for both get secret by name --- .../src/server/routes/v3/secret-router.ts | 38 +++++++++++---- .../src/services/secret/secret-service.ts | 46 +++++++++++++------ .../src/services/secret/secret-types.ts | 2 + 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/backend-pg/src/server/routes/v3/secret-router.ts b/backend-pg/src/server/routes/v3/secret-router.ts index 2c171020c..064e858c6 100644 --- a/backend-pg/src/server/routes/v3/secret-router.ts +++ b/backend-pg/src/server/routes/v3/secret-router.ts @@ -71,7 +71,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actor: req.permission.type, environment, - projectId: workspaceId as string, + projectId: workspaceId, path: secretPath, includeImports: req.query.include_imports }); @@ -100,9 +100,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretName: z.string().trim() }), querystring: z.object({ - workspaceId: z.string().trim(), - environment: z.string().trim(), + workspaceId: z.string().trim().optional(), + environment: z.string().trim().optional(), secretPath: z.string().trim().default("/"), + version: z.coerce.number().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared), include_imports: z .enum(["true", "false"]) @@ -122,15 +123,30 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { AuthMode.IDENTITY_ACCESS_TOKEN ]), handler: async (req) => { - const secret = await server.services.secret.getASecretRaw({ + let { secretPath, environment, workspaceId } = req.query; + if (req.auth.actor === ActorType.SERVICE) { + const scope = ServiceTokenScopes.parse(req.auth.serviceToken.scopes); + const isSingleScope = scope.length === 1; + if (isSingleScope && !picomatch.scan(scope[0].secretPath).isGlob) { + secretPath = scope[0].secretPath; + environment = scope[0].environment; + workspaceId = req.auth.serviceToken.projectId; + } + } + + if (!workspaceId || !environment) + throw new BadRequestError({ message: "Missing workspace id or environment" }); + + const secret = await server.services.secret.getSecretByNameRaw({ actorId: req.permission.id, actor: req.permission.type, - environment: req.query.environment, - projectId: req.query.workspaceId, - path: req.query.secretPath, + environment, + projectId: workspaceId, + path: secretPath, secretName: req.params.secretName, type: req.query.type, - includeImports: req.query.include_imports + includeImports: req.query.include_imports, + version: req.query.version }); await server.services.auditLog.createAuditLog({ @@ -411,6 +427,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { environment: z.string().trim(), secretPath: z.string().trim().default("/"), type: z.nativeEnum(SecretType).default(SecretType.Shared), + version: z.coerce.number().optional(), include_imports: z .enum(["true", "false"]) .default("false") @@ -429,7 +446,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { AuthMode.IDENTITY_ACCESS_TOKEN ]), handler: async (req) => { - const secret = await server.services.secret.getASecret({ + const secret = await server.services.secret.getSecretByName({ actorId: req.permission.id, actor: req.permission.type, environment: req.query.environment, @@ -437,7 +454,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { path: req.query.secretPath, secretName: req.params.secretName, type: req.query.type, - includeImports: req.query.include_imports + includeImports: req.query.include_imports, + version: req.query.version }); await server.services.auditLog.createAuditLog({ diff --git a/backend-pg/src/services/secret/secret-service.ts b/backend-pg/src/services/secret/secret-service.ts index a4113d979..d85e582c3 100644 --- a/backend-pg/src/services/secret/secret-service.ts +++ b/backend-pg/src/services/secret/secret-service.ts @@ -1,6 +1,12 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { SecretEncryptionAlgo, SecretKeyEncoding, SecretType, TableName } from "@app/db/schemas"; +import { + SecretEncryptionAlgo, + SecretKeyEncoding, + SecretsSchema, + SecretType, + TableName +} from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, @@ -530,7 +536,7 @@ export const secretServiceFactory = ({ return { secrets: secrets.map((el) => ({ ...el, workspace: projectId, environment })) }; }; - const getASecret = async ({ + const getSecretByName = async ({ actorId, actor, projectId, @@ -538,6 +544,7 @@ export const secretServiceFactory = ({ path, type, secretName, + version, includeImports }: TGetASecretDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); @@ -551,12 +558,21 @@ export const secretServiceFactory = ({ const secretBlindIndex = await interalGenSecBlindIndexByName(projectId, secretName); - const secret = await secretDal.findOne({ - folderId, - type, - userId: type === SecretType.Personal ? actorId : null, - secretBlindIndex - }); + const secret = await (typeof version !== undefined + ? secretDal.findOne({ + folderId, + type, + userId: type === SecretType.Personal ? actorId : null, + secretBlindIndex + }) + : secretVersionDal + .findOne({ + folderId, + type, + userId: type === SecretType.Personal ? actorId : null, + secretBlindIndex + }) + .then((el) => SecretsSchema.parse({ ...el, id: el.secretId }))); // now if secret is not found // then search for imported secrets // here we consider the import order also thus starting from bottom @@ -831,7 +847,7 @@ export const secretServiceFactory = ({ }; }; - const getASecretRaw = async ({ + const getSecretByNameRaw = async ({ type, path, actor, @@ -839,13 +855,14 @@ export const secretServiceFactory = ({ projectId, actorId, secretName, - includeImports + includeImports, + version }: TGetASecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); - const secret = await getASecret({ + const secret = await getSecretByName({ actorId, projectId, environment, @@ -853,7 +870,8 @@ export const secretServiceFactory = ({ path, secretName, type, - includeImports + includeImports, + version }); return decryptSecretRaw(secret, botKey); }; @@ -1007,10 +1025,10 @@ export const secretServiceFactory = ({ createManySecret, updateManySecret, deleteManySecret, - getASecret, + getSecretByName, getSecrets, getSecretsRaw, - getASecretRaw, + getSecretByNameRaw, createSecretRaw, updateSecretRaw, deleteSecretRaw, diff --git a/backend-pg/src/services/secret/secret-types.ts b/backend-pg/src/services/secret/secret-types.ts index 54062309d..ff6c72aed 100644 --- a/backend-pg/src/services/secret/secret-types.ts +++ b/backend-pg/src/services/secret/secret-types.ts @@ -76,6 +76,7 @@ export type TGetASecretDTO = { environment: string; type: "shared" | "personal"; includeImports?: boolean; + version?: number; } & TProjectPermission; export type TCreateBulkSecretDTO = { @@ -145,6 +146,7 @@ export type TGetASecretRawDTO = { environment: string; type: "shared" | "personal"; includeImports?: boolean; + version?: number; } & TProjectPermission; export type TCreateSecretRawDTO = TProjectPermission & {