diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index e9e98bffe..7a3a8d802 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -13,7 +13,7 @@ module.exports = { tsconfigRootDir: __dirname }, rules: { - // "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-function": "off", "consistent-return": "off", // my style "import/order": "off", // for simple-import-order "import/prefer-default-export": "off", // why diff --git a/backend/src/db/schemas/identity-access-tokens.ts b/backend/src/db/schemas/identity-access-tokens.ts index 62f74f4aa..d36291ab9 100644 --- a/backend/src/db/schemas/identity-access-tokens.ts +++ b/backend/src/db/schemas/identity-access-tokens.ts @@ -8,7 +8,7 @@ import { z } from "zod"; import { TImmutableDBKeys } from "./models"; export const IdentityAccessTokensSchema = z.object({ - id: z.string().uuid(), + id: z.string(), accessTokenTTL: z.coerce.number().default(2592000), accessTokenMaxTTL: z.coerce.number().default(2592000), accessTokenNumUses: z.coerce.number().default(0), diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index e0f70d1c0..539217c00 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -13,7 +13,7 @@ export const OrganizationsSchema = z.object({ customerId: z.string().nullable().optional(), slug: z.string(), createdAt: z.date(), - updatedAt: z.date() + updatedAt: z.date(), }); export type TOrganizations = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests-secrets.ts b/backend/src/db/schemas/secret-approval-requests-secrets.ts index 2fe6c6692..db98bda17 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestsSecretsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1).nullable().optional(), - secretBlindIndex: z.string(), + secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), secretKeyTag: z.string(), diff --git a/backend/src/db/schemas/user-encryption-keys.ts b/backend/src/db/schemas/user-encryption-keys.ts index 41a7ae57d..938b93aaf 100644 --- a/backend/src/db/schemas/user-encryption-keys.ts +++ b/backend/src/db/schemas/user-encryption-keys.ts @@ -12,9 +12,9 @@ export const UserEncryptionKeysSchema = z.object({ clientPublicKey: z.string().nullable().optional(), serverPrivateKey: z.string().nullable().optional(), encryptionVersion: z.number().default(2).nullable().optional(), - protectedKey: z.string().nullable(), - protectedKeyIV: z.string().nullable(), - protectedKeyTag: z.string().nullable(), + protectedKey: z.string().nullable().optional(), + protectedKeyIV: z.string().nullable().optional(), + protectedKeyTag: z.string().nullable().optional(), publicKey: z.string(), encryptedPrivateKey: z.string(), iv: z.string(), diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 9680a6271..81be86304 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -269,7 +269,14 @@ export const secretApprovalRequestServiceFactory = ({ const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({ folderId, - inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => ({ secretBlindIndex })) + inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) }); secretCreationCommits .filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""]) @@ -291,7 +298,14 @@ export const secretApprovalRequestServiceFactory = ({ ({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex ) - .map(({ secretBlindIndex }) => ({ secretBlindIndex })) + .map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) }); secretUpdationCommits .filter( @@ -381,10 +395,14 @@ export const secretApprovalRequestServiceFactory = ({ folderId, tx, actorId: "", - inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => ({ - secretBlindIndex, - type: SecretType.Shared - })) + inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex, type: SecretType.Shared }; + }) }) : []; const updatedSecretApproval = await secretApprovalRequestDAL.updateById( @@ -638,7 +656,13 @@ export const secretApprovalRequestServiceFactory = ({ ), tx ); - const commitsGroupByBlindIndex = groupBy(approvalCommits, (i) => i.secretBlindIndex); + + const commitsGroupByBlindIndex = groupBy(approvalCommits, (i) => { + if (!i.secretBlindIndex) { + throw new BadRequestError({ message: "Missing secret blind index" }); + } + return i.secretBlindIndex; + }); if (tagIds.length) { await secretApprovalRequestSecretDAL.insertApprovalSecretTags( Object.keys(commitTagIds).flatMap((blindIndex) => diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 19bf09727..16d32aada 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -92,7 +92,10 @@ import { serviceTokenDALFactory } from "@app/services/service-token/service-toke import { serviceTokenServiceFactory } from "@app/services/service-token/service-token-service"; import { TSmtpService } from "@app/services/smtp/smtp-service"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; -import { getServerCfg, superAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; +import { + getServerCfg, + superAdminServiceFactory +} from "@app/services/super-admin/super-admin-service"; import { telemetryServiceFactory } from "@app/services/telemetry/telemetry-service"; import { userDALFactory } from "@app/services/user/user-dal"; import { userServiceFactory } from "@app/services/user/user-service"; @@ -420,6 +423,7 @@ export const registerRoutes = async ( const serviceTokenService = serviceTokenServiceFactory({ projectEnvDAL, serviceTokenDAL, + userDAL, permissionService }); @@ -516,14 +520,14 @@ export const registerRoutes = async ( }, handler: () => { const cfg = getConfig(); - const serverCfg = getServerCfg() + const serverCfg = getServerCfg(); return { date: new Date(), message: "Ok" as const, emailConfigured: cfg.isSmtpConfigured, inviteOnlySignup: Boolean(serverCfg.allowSignUp), redisConfigured: cfg.isRedisConfigured, - secretScanningConfigured: cfg.isSecretScanningConfigured, + secretScanningConfigured: cfg.isSecretScanningConfigured }; } }); diff --git a/backend/src/server/routes/sanitizedSchemas.ts b/backend/src/server/routes/sanitizedSchemas.ts index cc9dedd45..afc12c081 100644 --- a/backend/src/server/routes/sanitizedSchemas.ts +++ b/backend/src/server/routes/sanitizedSchemas.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema } from "@app/db/schemas"; +import { IntegrationAuthsSchema, SecretApprovalPoliciesSchema, UsersSchema } from "@app/db/schemas"; // sometimes the return data must be santizied to avoid leaking important values // always prefer pick over omit in zod @@ -28,6 +28,18 @@ export const sapPubSchema = SecretApprovalPoliciesSchema.merge( }) ); +export const sanitizedServiceTokenUserSchema = UsersSchema.pick({ + authMethods: true, + id: true, + createdAt: true, + updatedAt: true, + devices: true, + email: true, + firstName: true, + lastName: true, + mfaMethods: true +}); + export const secretRawSchema = z.object({ id: z.string(), _id: z.string(), diff --git a/backend/src/server/routes/v2/mfa-router.ts b/backend/src/server/routes/v2/mfa-router.ts index 6efda7221..02006a470 100644 --- a/backend/src/server/routes/v2/mfa-router.ts +++ b/backend/src/server/routes/v2/mfa-router.ts @@ -86,7 +86,13 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { secure: appCfg.HTTPS_ENABLED }); - return { token: token.access, ...user }; + return { + ...user, + token: token.access, + protectedKey: user.protectedKey || null, + protectedKeyIV: user.protectedKeyIV || null, + protectedKeyTag: user.protectedKeyTag || null + }; } }); }; diff --git a/backend/src/server/routes/v2/service-token-router.ts b/backend/src/server/routes/v2/service-token-router.ts index 31f5c22d6..52e4d7334 100644 --- a/backend/src/server/routes/v2/service-token-router.ts +++ b/backend/src/server/routes/v2/service-token-router.ts @@ -6,6 +6,8 @@ import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; +import { sanitizedServiceTokenUserSchema } from "../sanitizedSchemas"; + export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({ secretHash: true, encryptedKey: true, @@ -20,15 +22,22 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]), schema: { response: { - 200: ServiceTokensSchema.merge(z.object({ workspace: z.string() })) + 200: ServiceTokensSchema.merge( + z.object({ + workspace: z.string(), + user: sanitizedServiceTokenUserSchema + }) + ) } }, handler: async (req) => { - const serviceTokenData = await server.services.serviceToken.getServiceToken({ + const { serviceToken, user } = await server.services.serviceToken.getServiceToken({ actorId: req.permission.id, actor: req.permission.type }); - return { ...serviceTokenData, workspace: serviceTokenData.projectId }; + + // We return the user here because older versions of the deprecated Python SDK depend on it to properly parse the API response. + return { ...serviceToken, workspace: serviceToken.projectId, user }; } }); diff --git a/backend/src/server/routes/v3/login-router.ts b/backend/src/server/routes/v3/login-router.ts index 22d7b8be7..1c513c183 100644 --- a/backend/src/server/routes/v3/login-router.ts +++ b/backend/src/server/routes/v3/login-router.ts @@ -96,9 +96,9 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => { encryptedPrivateKey: data.user.encryptedPrivateKey, iv: data.user.iv, tag: data.user.tag, - protectedKey: data.user.protectedKey, - protectedKeyIV: data.user.protectedKeyIV, - protectedKeyTag: data.user.protectedKeyTag + protectedKey: data.user.protectedKey || null, + protectedKeyIV: data.user.protectedKeyIV || null, + protectedKeyTag: data.user.protectedKeyTag || null } as const; } }); diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index cdb92fae6..99dc5ddfb 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -442,6 +442,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secrets: SecretsSchema.omit({ secretBlindIndex: true }) .merge( z.object({ + workspace: z.string(), + environment: z.string(), tags: SecretTagsSchema.pick({ id: true, slug: true, @@ -529,7 +531,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + workspace: z.string(), + environment: z.string() + }) + ) }) } }, @@ -610,7 +617,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + workspace: z.string(), + environment: z.string() + }) + ) }), z .object({ approval: SecretApprovalRequestsSchema }) @@ -780,7 +792,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + workspace: z.string(), + environment: z.string() + }) + ) }), z .object({ approval: SecretApprovalRequestsSchema }) @@ -944,7 +961,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { response: { 200: z.union([ z.object({ - secret: SecretsSchema.omit({ secretBlindIndex: true }) + secret: SecretsSchema.omit({ secretBlindIndex: true }).merge( + z.object({ + workspace: z.string(), + environment: z.string() + }) + ) }), z .object({ approval: SecretApprovalRequestsSchema }) diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 48ead50e6..63d5f6aac 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -13,6 +13,7 @@ import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "../auth/auth-type"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; +import { TUserDALFactory } from "../user/user-dal"; import { TServiceTokenDALFactory } from "./service-token-dal"; import { TCreateServiceTokenDTO, @@ -23,6 +24,7 @@ import { type TServiceTokenServiceFactoryDep = { serviceTokenDAL: TServiceTokenDALFactory; + userDAL: TUserDALFactory; permissionService: Pick; projectEnvDAL: Pick; }; @@ -31,6 +33,7 @@ export type TServiceTokenServiceFactory = ReturnType { @@ -51,14 +54,14 @@ export const serviceTokenServiceFactory = ({ ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens ); - + scopes.forEach(({ environment, secretPath }) => { ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); - }) - + }); + const appCfg = getConfig(); // validates env @@ -119,7 +122,10 @@ export const serviceTokenServiceFactory = ({ const serviceToken = await serviceTokenDAL.findById(actorId); if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); - return serviceToken; + const serviceTokenUser = await userDAL.findById(serviceToken.createdBy); + if (!serviceTokenUser) throw new BadRequestError({ message: "Service token user not found" }); + + return { serviceToken, user: serviceTokenUser }; }; const getProjectServiceTokens = async ({