Merge pull request #1343 from Infisical/daniel/pg-endpoint-fixes

(Postgres Fix): Secret endpoints return data
This commit is contained in:
Maidul Islam
2024-01-29 06:23:55 -05:00
committed by GitHub
13 changed files with 116 additions and 33 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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<typeof OrganizationsSchema>;

View File

@@ -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(),

View File

@@ -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(),

View File

@@ -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) =>

View File

@@ -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
};
}
});

View File

@@ -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(),

View File

@@ -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
};
}
});
};

View File

@@ -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 };
}
});

View File

@@ -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;
}
});

View File

@@ -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 })

View File

@@ -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<TPermissionServiceFactory, "getProjectPermission">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findBySlugs">;
};
@@ -31,6 +33,7 @@ export type TServiceTokenServiceFactory = ReturnType<typeof serviceTokenServiceF
export const serviceTokenServiceFactory = ({
serviceTokenDAL,
userDAL,
permissionService,
projectEnvDAL
}: TServiceTokenServiceFactoryDep) => {
@@ -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 ({