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
+1 -1
View File
@@ -13,7 +13,7 @@ module.exports = {
tsconfigRootDir: __dirname tsconfigRootDir: __dirname
}, },
rules: { rules: {
// "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"consistent-return": "off", // my style "consistent-return": "off", // my style
"import/order": "off", // for simple-import-order "import/order": "off", // for simple-import-order
"import/prefer-default-export": "off", // why "import/prefer-default-export": "off", // why
@@ -8,7 +8,7 @@ import { z } from "zod";
import { TImmutableDBKeys } from "./models"; import { TImmutableDBKeys } from "./models";
export const IdentityAccessTokensSchema = z.object({ export const IdentityAccessTokensSchema = z.object({
id: z.string().uuid(), id: z.string(),
accessTokenTTL: z.coerce.number().default(2592000), accessTokenTTL: z.coerce.number().default(2592000),
accessTokenMaxTTL: z.coerce.number().default(2592000), accessTokenMaxTTL: z.coerce.number().default(2592000),
accessTokenNumUses: z.coerce.number().default(0), accessTokenNumUses: z.coerce.number().default(0),
+1 -1
View File
@@ -13,7 +13,7 @@ export const OrganizationsSchema = z.object({
customerId: z.string().nullable().optional(), customerId: z.string().nullable().optional(),
slug: z.string(), slug: z.string(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date() updatedAt: z.date(),
}); });
export type TOrganizations = z.infer<typeof OrganizationsSchema>; export type TOrganizations = z.infer<typeof OrganizationsSchema>;
@@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
export const SecretApprovalRequestsSecretsSchema = z.object({ export const SecretApprovalRequestsSecretsSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
version: z.number().default(1).nullable().optional(), version: z.number().default(1).nullable().optional(),
secretBlindIndex: z.string(), secretBlindIndex: z.string().nullable().optional(),
secretKeyCiphertext: z.string(), secretKeyCiphertext: z.string(),
secretKeyIV: z.string(), secretKeyIV: z.string(),
secretKeyTag: z.string(), secretKeyTag: z.string(),
@@ -12,9 +12,9 @@ export const UserEncryptionKeysSchema = z.object({
clientPublicKey: z.string().nullable().optional(), clientPublicKey: z.string().nullable().optional(),
serverPrivateKey: z.string().nullable().optional(), serverPrivateKey: z.string().nullable().optional(),
encryptionVersion: z.number().default(2).nullable().optional(), encryptionVersion: z.number().default(2).nullable().optional(),
protectedKey: z.string().nullable(), protectedKey: z.string().nullable().optional(),
protectedKeyIV: z.string().nullable(), protectedKeyIV: z.string().nullable().optional(),
protectedKeyTag: z.string().nullable(), protectedKeyTag: z.string().nullable().optional(),
publicKey: z.string(), publicKey: z.string(),
encryptedPrivateKey: z.string(), encryptedPrivateKey: z.string(),
iv: z.string(), iv: z.string(),
@@ -269,7 +269,14 @@ export const secretApprovalRequestServiceFactory = ({
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } =
await secretService.fnSecretBlindIndexCheckV2({ await secretService.fnSecretBlindIndexCheckV2({
folderId, folderId,
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => ({ secretBlindIndex })) inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
if (!secretBlindIndex) {
throw new BadRequestError({
message: "Missing secret blind index"
});
}
return { secretBlindIndex };
})
}); });
secretCreationCommits secretCreationCommits
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""]) .filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
@@ -291,7 +298,14 @@ export const secretApprovalRequestServiceFactory = ({
({ secretBlindIndex, secret }) => ({ secretBlindIndex, secret }) =>
secret && secret.secretBlindIndex !== secretBlindIndex secret && secret.secretBlindIndex !== secretBlindIndex
) )
.map(({ secretBlindIndex }) => ({ secretBlindIndex })) .map(({ secretBlindIndex }) => {
if (!secretBlindIndex) {
throw new BadRequestError({
message: "Missing secret blind index"
});
}
return { secretBlindIndex };
})
}); });
secretUpdationCommits secretUpdationCommits
.filter( .filter(
@@ -381,10 +395,14 @@ export const secretApprovalRequestServiceFactory = ({
folderId, folderId,
tx, tx,
actorId: "", actorId: "",
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => ({ inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
secretBlindIndex, if (!secretBlindIndex) {
type: SecretType.Shared throw new BadRequestError({
})) message: "Missing secret blind index"
});
}
return { secretBlindIndex, type: SecretType.Shared };
})
}) })
: []; : [];
const updatedSecretApproval = await secretApprovalRequestDAL.updateById( const updatedSecretApproval = await secretApprovalRequestDAL.updateById(
@@ -638,7 +656,13 @@ export const secretApprovalRequestServiceFactory = ({
), ),
tx 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) { if (tagIds.length) {
await secretApprovalRequestSecretDAL.insertApprovalSecretTags( await secretApprovalRequestSecretDAL.insertApprovalSecretTags(
Object.keys(commitTagIds).flatMap((blindIndex) => Object.keys(commitTagIds).flatMap((blindIndex) =>
+7 -3
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 { serviceTokenServiceFactory } from "@app/services/service-token/service-token-service";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; 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 { telemetryServiceFactory } from "@app/services/telemetry/telemetry-service";
import { userDALFactory } from "@app/services/user/user-dal"; import { userDALFactory } from "@app/services/user/user-dal";
import { userServiceFactory } from "@app/services/user/user-service"; import { userServiceFactory } from "@app/services/user/user-service";
@@ -420,6 +423,7 @@ export const registerRoutes = async (
const serviceTokenService = serviceTokenServiceFactory({ const serviceTokenService = serviceTokenServiceFactory({
projectEnvDAL, projectEnvDAL,
serviceTokenDAL, serviceTokenDAL,
userDAL,
permissionService permissionService
}); });
@@ -516,14 +520,14 @@ export const registerRoutes = async (
}, },
handler: () => { handler: () => {
const cfg = getConfig(); const cfg = getConfig();
const serverCfg = getServerCfg() const serverCfg = getServerCfg();
return { return {
date: new Date(), date: new Date(),
message: "Ok" as const, message: "Ok" as const,
emailConfigured: cfg.isSmtpConfigured, emailConfigured: cfg.isSmtpConfigured,
inviteOnlySignup: Boolean(serverCfg.allowSignUp), inviteOnlySignup: Boolean(serverCfg.allowSignUp),
redisConfigured: cfg.isRedisConfigured, redisConfigured: cfg.isRedisConfigured,
secretScanningConfigured: cfg.isSecretScanningConfigured, secretScanningConfigured: cfg.isSecretScanningConfigured
}; };
} }
}); });
+13 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod"; 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 // sometimes the return data must be santizied to avoid leaking important values
// always prefer pick over omit in zod // 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({ export const secretRawSchema = z.object({
id: z.string(), id: z.string(),
_id: z.string(), _id: z.string(),
+7 -1
View File
@@ -86,7 +86,13 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
secure: appCfg.HTTPS_ENABLED 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
};
} }
}); });
}; };
@@ -6,6 +6,8 @@ import { removeTrailingSlash } from "@app/lib/fn";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { sanitizedServiceTokenUserSchema } from "../sanitizedSchemas";
export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({ export const sanitizedServiceTokenSchema = ServiceTokensSchema.omit({
secretHash: true, secretHash: true,
encryptedKey: true, encryptedKey: true,
@@ -20,15 +22,22 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) =>
onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]), onRequest: verifyAuth([AuthMode.SERVICE_TOKEN]),
schema: { schema: {
response: { response: {
200: ServiceTokensSchema.merge(z.object({ workspace: z.string() })) 200: ServiceTokensSchema.merge(
z.object({
workspace: z.string(),
user: sanitizedServiceTokenUserSchema
})
)
} }
}, },
handler: async (req) => { handler: async (req) => {
const serviceTokenData = await server.services.serviceToken.getServiceToken({ const { serviceToken, user } = await server.services.serviceToken.getServiceToken({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type 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 };
} }
}); });
+3 -3
View File
@@ -96,9 +96,9 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
encryptedPrivateKey: data.user.encryptedPrivateKey, encryptedPrivateKey: data.user.encryptedPrivateKey,
iv: data.user.iv, iv: data.user.iv,
tag: data.user.tag, tag: data.user.tag,
protectedKey: data.user.protectedKey, protectedKey: data.user.protectedKey || null,
protectedKeyIV: data.user.protectedKeyIV, protectedKeyIV: data.user.protectedKeyIV || null,
protectedKeyTag: data.user.protectedKeyTag protectedKeyTag: data.user.protectedKeyTag || null
} as const; } as const;
} }
}); });
+26 -4
View File
@@ -442,6 +442,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: SecretsSchema.omit({ secretBlindIndex: true }) secrets: SecretsSchema.omit({ secretBlindIndex: true })
.merge( .merge(
z.object({ z.object({
workspace: z.string(),
environment: z.string(),
tags: SecretTagsSchema.pick({ tags: SecretTagsSchema.pick({
id: true, id: true,
slug: true, slug: true,
@@ -529,7 +531,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 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: { response: {
200: z.union([ 200: z.union([
z.object({ z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true }) secret: SecretsSchema.omit({ secretBlindIndex: true }).merge(
z.object({
workspace: z.string(),
environment: z.string()
})
)
}), }),
z z
.object({ approval: SecretApprovalRequestsSchema }) .object({ approval: SecretApprovalRequestsSchema })
@@ -780,7 +792,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
response: { response: {
200: z.union([ 200: z.union([
z.object({ z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true }) secret: SecretsSchema.omit({ secretBlindIndex: true }).merge(
z.object({
workspace: z.string(),
environment: z.string()
})
)
}), }),
z z
.object({ approval: SecretApprovalRequestsSchema }) .object({ approval: SecretApprovalRequestsSchema })
@@ -944,7 +961,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
response: { response: {
200: z.union([ 200: z.union([
z.object({ z.object({
secret: SecretsSchema.omit({ secretBlindIndex: true }) secret: SecretsSchema.omit({ secretBlindIndex: true }).merge(
z.object({
workspace: z.string(),
environment: z.string()
})
)
}), }),
z z
.object({ approval: SecretApprovalRequestsSchema }) .object({ approval: SecretApprovalRequestsSchema })
@@ -13,6 +13,7 @@ import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { ActorType } from "../auth/auth-type"; import { ActorType } from "../auth/auth-type";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TUserDALFactory } from "../user/user-dal";
import { TServiceTokenDALFactory } from "./service-token-dal"; import { TServiceTokenDALFactory } from "./service-token-dal";
import { import {
TCreateServiceTokenDTO, TCreateServiceTokenDTO,
@@ -23,6 +24,7 @@ import {
type TServiceTokenServiceFactoryDep = { type TServiceTokenServiceFactoryDep = {
serviceTokenDAL: TServiceTokenDALFactory; serviceTokenDAL: TServiceTokenDALFactory;
userDAL: TUserDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findBySlugs">; projectEnvDAL: Pick<TProjectEnvDALFactory, "findBySlugs">;
}; };
@@ -31,6 +33,7 @@ export type TServiceTokenServiceFactory = ReturnType<typeof serviceTokenServiceF
export const serviceTokenServiceFactory = ({ export const serviceTokenServiceFactory = ({
serviceTokenDAL, serviceTokenDAL,
userDAL,
permissionService, permissionService,
projectEnvDAL projectEnvDAL
}: TServiceTokenServiceFactoryDep) => { }: TServiceTokenServiceFactoryDep) => {
@@ -57,7 +60,7 @@ export const serviceTokenServiceFactory = ({
ProjectPermissionActions.Create, ProjectPermissionActions.Create,
subject(ProjectPermissionSub.Secrets, { environment, secretPath }) subject(ProjectPermissionSub.Secrets, { environment, secretPath })
); );
}) });
const appCfg = getConfig(); const appCfg = getConfig();
@@ -119,7 +122,10 @@ export const serviceTokenServiceFactory = ({
const serviceToken = await serviceTokenDAL.findById(actorId); const serviceToken = await serviceTokenDAL.findById(actorId);
if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); 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 ({ const getProjectServiceTokens = async ({