mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 19:44:52 +00:00
feature: secret sync base + aws parameter store & github
This commit is contained in:
@@ -196,6 +196,9 @@ import { secretImportDALFactory } from "@app/services/secret-import/secret-impor
|
||||
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||
import { secretSharingDALFactory } from "@app/services/secret-sharing/secret-sharing-dal";
|
||||
import { secretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
||||
import { secretSyncDALFactory } from "@app/services/secret-sync/secret-sync-dal";
|
||||
import { secretSyncQueueFactory } from "@app/services/secret-sync/secret-sync-queue";
|
||||
import { secretSyncServiceFactory } from "@app/services/secret-sync/secret-sync-service";
|
||||
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||
import { secretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
|
||||
@@ -318,6 +321,7 @@ export const registerRoutes = async (
|
||||
const trustedIpDAL = trustedIpDALFactory(db);
|
||||
const telemetryDAL = telemetryDALFactory(db);
|
||||
const appConnectionDAL = appConnectionDALFactory(db);
|
||||
const secretSyncDAL = secretSyncDALFactory(db, folderDAL);
|
||||
|
||||
// ee db layer ops
|
||||
const permissionDAL = permissionDALFactory(db);
|
||||
@@ -824,6 +828,29 @@ export const registerRoutes = async (
|
||||
kmsService
|
||||
});
|
||||
|
||||
const secretSyncQueue = secretSyncQueueFactory({
|
||||
queueService,
|
||||
secretSyncDAL,
|
||||
folderDAL,
|
||||
secretImportDAL,
|
||||
secretV2BridgeDAL,
|
||||
kmsService,
|
||||
keyStore,
|
||||
auditLogService,
|
||||
smtpService,
|
||||
projectDAL,
|
||||
projectMembershipDAL,
|
||||
projectBotDAL,
|
||||
secretDAL,
|
||||
secretBlindIndexDAL,
|
||||
secretVersionDAL,
|
||||
secretTagDAL,
|
||||
secretVersionTagDAL,
|
||||
secretVersionV2BridgeDAL,
|
||||
secretVersionTagV2BridgeDAL,
|
||||
resourceMetadataDAL
|
||||
});
|
||||
|
||||
const secretQueueService = secretQueueFactory({
|
||||
keyStore,
|
||||
queueService,
|
||||
@@ -858,7 +885,8 @@ export const registerRoutes = async (
|
||||
projectKeyDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
orgService,
|
||||
resourceMetadataDAL
|
||||
resourceMetadataDAL,
|
||||
secretSyncQueue
|
||||
});
|
||||
|
||||
const projectService = projectServiceFactory({
|
||||
@@ -1368,8 +1396,17 @@ export const registerRoutes = async (
|
||||
const appConnectionService = appConnectionServiceFactory({
|
||||
appConnectionDAL,
|
||||
permissionService,
|
||||
kmsService,
|
||||
licenseService
|
||||
kmsService
|
||||
});
|
||||
|
||||
const secretSyncService = secretSyncServiceFactory({
|
||||
secretSyncDAL,
|
||||
permissionService,
|
||||
appConnectionService,
|
||||
folderDAL,
|
||||
secretSyncQueue,
|
||||
projectBotService,
|
||||
keyStore
|
||||
});
|
||||
|
||||
await superAdminService.initServerCfg();
|
||||
@@ -1469,7 +1506,8 @@ export const registerRoutes = async (
|
||||
externalGroupOrgRoleMapping: externalGroupOrgRoleMappingService,
|
||||
projectTemplate: projectTemplateService,
|
||||
totp: totpService,
|
||||
appConnection: appConnectionService
|
||||
appConnection: appConnectionService,
|
||||
secretSync: secretSyncService
|
||||
});
|
||||
|
||||
const cronJobs: CronJob[] = [];
|
||||
|
||||
+49
-10
@@ -15,7 +15,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
app,
|
||||
createSchema,
|
||||
updateSchema,
|
||||
responseSchema
|
||||
sanitizedResponseSchema
|
||||
}: {
|
||||
app: AppConnection;
|
||||
server: FastifyZodProvider;
|
||||
@@ -26,7 +26,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
description?: string | null;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"]; description?: string | null }>;
|
||||
responseSchema: z.ZodTypeAny;
|
||||
sanitizedResponseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
const appName = APP_CONNECTION_NAME_MAP[app];
|
||||
|
||||
@@ -39,7 +39,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
schema: {
|
||||
description: `List the ${appName} Connections for the current organization.`,
|
||||
response: {
|
||||
200: z.object({ appConnections: responseSchema.array() })
|
||||
200: z.object({ appConnections: sanitizedResponseSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -63,6 +63,44 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/available",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: `List the ${appName} Connections the current user has permission to establish connections with.`,
|
||||
response: {
|
||||
200: z.object({
|
||||
appConnections: z.object({ app: z.literal(app), name: z.string(), id: z.string().uuid() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const appConnections = await server.services.appConnection.listAvailableAppConnectionsForUser(
|
||||
app,
|
||||
req.permission
|
||||
);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: req.permission.orgId,
|
||||
event: {
|
||||
type: EventType.GET_AVAILABLE_APP_CONNECTIONS_DETAILS,
|
||||
metadata: {
|
||||
app,
|
||||
count: appConnections.length,
|
||||
connectionIds: appConnections.map((connection) => connection.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { appConnections };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:connectionId",
|
||||
@@ -75,7 +113,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
connectionId: z.string().uuid().describe(AppConnections.GET_BY_ID(app).connectionId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -114,11 +152,12 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
params: z.object({
|
||||
connectionName: z
|
||||
.string()
|
||||
.min(0, "Connection name required")
|
||||
.trim()
|
||||
.min(1, "Connection name required")
|
||||
.describe(AppConnections.GET_BY_NAME(app).connectionName)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -158,7 +197,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
} ${appName} Connection for the current organization.`,
|
||||
body: createSchema,
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -168,7 +207,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
const appConnection = (await server.services.appConnection.createAppConnection(
|
||||
{ name, method, app, credentials, description },
|
||||
req.permission
|
||||
)) as TAppConnection;
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
@@ -201,7 +240,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
}),
|
||||
body: updateSchema,
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -244,7 +283,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
connectionId: z.string().uuid().describe(AppConnections.DELETE(app).connectionId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ appConnection: responseSchema })
|
||||
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
@@ -1,17 +0,0 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateGitHubConnectionSchema,
|
||||
SanitizedGitHubConnectionSchema,
|
||||
UpdateGitHubConnectionSchema
|
||||
} from "@app/services/app-connection/github";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerGitHubConnectionRouter = async (server: FastifyZodProvider) =>
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.GitHub,
|
||||
server,
|
||||
responseSchema: SanitizedGitHubConnectionSchema,
|
||||
createSchema: CreateGitHubConnectionSchema,
|
||||
updateSchema: UpdateGitHubConnectionSchema
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import { registerAwsConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/aws-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/github-connection-router";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
export const APP_CONNECTION_REGISTER_MAP: Record<AppConnection, (server: FastifyZodProvider) => Promise<void>> = {
|
||||
[AppConnection.AWS]: registerAwsConnectionRouter,
|
||||
[AppConnection.GitHub]: registerGitHubConnectionRouter
|
||||
};
|
||||
+1
-1
@@ -11,7 +11,7 @@ export const registerAwsConnectionRouter = async (server: FastifyZodProvider) =>
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.AWS,
|
||||
server,
|
||||
responseSchema: SanitizedAwsConnectionSchema,
|
||||
sanitizedResponseSchema: SanitizedAwsConnectionSchema,
|
||||
createSchema: CreateAwsConnectionSchema,
|
||||
updateSchema: UpdateAwsConnectionSchema
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateGitHubConnectionSchema,
|
||||
SanitizedGitHubConnectionSchema,
|
||||
UpdateGitHubConnectionSchema
|
||||
} from "@app/services/app-connection/github";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerGitHubConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.GitHub,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedGitHubConnectionSchema,
|
||||
createSchema: CreateGitHubConnectionSchema,
|
||||
updateSchema: UpdateGitHubConnectionSchema
|
||||
});
|
||||
|
||||
// The below endpoints are not exposed and for Infisical App use
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/repositories`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
repositories: z
|
||||
.object({ id: z.number(), name: z.string(), owner: z.object({ login: z.string(), id: z.number() }) })
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const repositories = await server.services.appConnection.github.listRepositories(connectionId, req.permission);
|
||||
|
||||
return { repositories };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/organizations`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
organizations: z.object({ id: z.number(), login: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const organizations = await server.services.appConnection.github.listOrganizations(connectionId, req.permission);
|
||||
|
||||
return { organizations };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/environments`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
querystring: z.object({
|
||||
repo: z.string().min(1, "Repository name is required"),
|
||||
owner: z.string().min(1, "Repository owner name is required")
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
environments: z.object({ id: z.number(), name: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
const { repo, owner } = req.query;
|
||||
|
||||
const environments = await server.services.appConnection.github.listEnvironments(
|
||||
{
|
||||
connectionId,
|
||||
repo,
|
||||
owner
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
return { environments };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,2 +1,12 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
import { registerAwsConnectionRouter } from "./aws-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||
|
||||
export * from "./app-connection-router";
|
||||
export * from "./apps";
|
||||
|
||||
export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server: FastifyZodProvider) => Promise<void>> =
|
||||
{
|
||||
[AppConnection.AWS]: registerAwsConnectionRouter,
|
||||
[AppConnection.GitHub]: registerGitHubConnectionRouter
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { APP_CONNECTION_REGISTER_MAP, registerAppConnectionRouter } from "@app/server/routes/v1/app-connection-routers";
|
||||
import {
|
||||
APP_CONNECTION_REGISTER_ROUTER_MAP,
|
||||
registerAppConnectionRouter
|
||||
} from "@app/server/routes/v1/app-connection-routers";
|
||||
import { registerCmekRouter } from "@app/server/routes/v1/cmek-router";
|
||||
import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router";
|
||||
import { registerSecretSyncRouter, SECRET_SYNC_REGISTER_ROUTER_MAP } from "@app/server/routes/v1/secret-sync-routers";
|
||||
|
||||
import { registerAdminRouter } from "./admin-router";
|
||||
import { registerAuthRoutes } from "./auth-router";
|
||||
@@ -113,12 +117,28 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
||||
await server.register(registerExternalGroupOrgRoleMappingRouter, { prefix: "/external-group-mappings" });
|
||||
|
||||
await server.register(
|
||||
async (appConnectionsRouter) => {
|
||||
await appConnectionsRouter.register(registerAppConnectionRouter);
|
||||
for await (const [app, router] of Object.entries(APP_CONNECTION_REGISTER_MAP)) {
|
||||
await appConnectionsRouter.register(router, { prefix: `/${app}` });
|
||||
async (appConnectionRouter) => {
|
||||
// register generic app connection endpoints
|
||||
await appConnectionRouter.register(registerAppConnectionRouter);
|
||||
|
||||
// register service specific endpoints (app-connections/aws, app-connections/github, etc.)
|
||||
for await (const [app, router] of Object.entries(APP_CONNECTION_REGISTER_ROUTER_MAP)) {
|
||||
await appConnectionRouter.register(router, { prefix: `/${app}` });
|
||||
}
|
||||
},
|
||||
{ prefix: "/app-connections" }
|
||||
);
|
||||
|
||||
await server.register(
|
||||
async (secretSyncRouter) => {
|
||||
// register generic secret sync endpoints
|
||||
await secretSyncRouter.register(registerSecretSyncRouter);
|
||||
|
||||
// register service specific secret sync endpoints (secret-syncs/aws-parameter-store, secret-syncs/github, etc.)
|
||||
for await (const [destination, router] of Object.entries(SECRET_SYNC_REGISTER_ROUTER_MAP)) {
|
||||
await secretSyncRouter.register(router, { prefix: `/${destination}` });
|
||||
}
|
||||
},
|
||||
{ prefix: "/secret-syncs" }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
AwsParameterStoreSyncSchema,
|
||||
CreateAwsParameterStoreSyncSchema,
|
||||
UpdateAwsParameterStoreSyncSchema
|
||||
} from "@app/services/secret-sync/aws-parameter-store";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerAwsParameterStoreSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.AWSParameterStore,
|
||||
server,
|
||||
responseSchema: AwsParameterStoreSyncSchema,
|
||||
createSchema: CreateAwsParameterStoreSyncSchema,
|
||||
updateSchema: UpdateAwsParameterStoreSyncSchema
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { CreateGitHubSyncSchema, GitHubSyncSchema, UpdateGitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerGitHubSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.GitHub,
|
||||
server,
|
||||
responseSchema: GitHubSyncSchema,
|
||||
createSchema: CreateGitHubSyncSchema,
|
||||
updateSchema: UpdateGitHubSyncSchema
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-router";
|
||||
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||
|
||||
export * from "./secret-sync-router";
|
||||
|
||||
export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: FastifyZodProvider) => Promise<void>> = {
|
||||
[SecretSync.AWSParameterStore]: registerAwsParameterStoreSyncRouter,
|
||||
[SecretSync.GitHub]: registerGitHubSyncRouter
|
||||
};
|
||||
@@ -0,0 +1,397 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { startsWithVowel } from "@app/lib/fn";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { SecretSync, SecretSyncImportBehavior } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
import { TSecretSync, TSecretSyncInput } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const registerSyncSecretsEndpoints = <T extends TSecretSync, I extends TSecretSyncInput>({
|
||||
server,
|
||||
destination,
|
||||
createSchema,
|
||||
updateSchema,
|
||||
responseSchema
|
||||
}: {
|
||||
destination: SecretSync;
|
||||
server: FastifyZodProvider;
|
||||
createSchema: z.ZodType<{
|
||||
name: string;
|
||||
environment: string;
|
||||
secretPath: string;
|
||||
projectId: string;
|
||||
connectionId: string;
|
||||
destinationConfig: I["destinationConfig"];
|
||||
syncOptions: I["syncOptions"];
|
||||
description?: string | null;
|
||||
isEnabled?: boolean;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{
|
||||
name?: string;
|
||||
environment?: string;
|
||||
secretPath?: string;
|
||||
destinationConfig?: I["destinationConfig"];
|
||||
syncOptions?: I["syncOptions"];
|
||||
description?: string | null;
|
||||
}>;
|
||||
responseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: `List the ${destinationName} Syncs for the specified project.`,
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1, "Project ID required").describe(SecretSyncs.LIST(destination).projectId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSyncs: responseSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const {
|
||||
query: { projectId }
|
||||
} = req;
|
||||
|
||||
const secretSyncs = (await server.services.secretSync.listSecretSyncsByProjectId(
|
||||
{ projectId, destination },
|
||||
req.permission
|
||||
)) as T[];
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_SYNCS,
|
||||
metadata: {
|
||||
destination,
|
||||
count: secretSyncs.length,
|
||||
syncIds: secretSyncs.map((connection) => connection.id)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSyncs };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:syncId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Get the specified ${destinationName} Sync by ID.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.GET_BY_ID(destination).syncId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
|
||||
const secretSync = (await server.services.secretSync.findSecretSyncById(
|
||||
{ syncId, destination },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: secretSync.projectId,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_SYNC,
|
||||
metadata: {
|
||||
syncId,
|
||||
destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/name/:syncName`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Get the specified ${destinationName} Sync by name and project ID.`,
|
||||
params: z.object({
|
||||
syncName: z.string().trim().min(1, "Sync name required").describe(SecretSyncs.GET_BY_NAME(destination).syncName)
|
||||
}),
|
||||
querystring: z.object({
|
||||
projectId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Project ID required")
|
||||
.describe(SecretSyncs.GET_BY_NAME(destination).projectId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncName } = req.params;
|
||||
const { projectId } = req.query;
|
||||
|
||||
const secretSync = (await server.services.secretSync.findSecretSyncByName(
|
||||
{ syncName, projectId, destination },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_SYNC,
|
||||
metadata: {
|
||||
syncId: secretSync.id,
|
||||
destination
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Create ${
|
||||
startsWithVowel(destinationName) ? "an" : "a"
|
||||
} ${destinationName} Sync for the specified project environment.`,
|
||||
body: createSchema,
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const secretSync = (await server.services.secretSync.createSecretSync(
|
||||
{ ...req.body, destination },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: secretSync.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_SECRET_SYNC,
|
||||
metadata: {
|
||||
syncId: secretSync.id,
|
||||
destination,
|
||||
...req.body
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:syncId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Update the specified ${destinationName} Connection.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.UPDATE(destination).syncId)
|
||||
}),
|
||||
body: updateSchema,
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
|
||||
const secretSync = (await server.services.secretSync.updateSecretSync(
|
||||
{ ...req.body, syncId, destination },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: secretSync.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_SECRET_SYNC,
|
||||
metadata: {
|
||||
syncId,
|
||||
destination,
|
||||
...req.body
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: `/:syncId`,
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Delete the specified ${destinationName} Connection.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.DELETE(destination).syncId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
|
||||
const secretSync = (await server.services.secretSync.deleteSecretSync(
|
||||
{ destination, syncId },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: req.permission.orgId,
|
||||
event: {
|
||||
type: EventType.DELETE_SECRET_SYNC,
|
||||
metadata: {
|
||||
destination,
|
||||
syncId
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:syncId/sync-secrets",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Trigger a sync for the specified ${destinationName} Sync.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.SYNC_SECRETS(destination).syncId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
|
||||
const secretSync = (await server.services.secretSync.triggerSecretSyncSyncSecretsById(
|
||||
{
|
||||
syncId,
|
||||
destination,
|
||||
auditLogInfo: req.auditLogInfo
|
||||
},
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:syncId/import-secrets",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Import secrets from the specified ${destinationName} Sync destination.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.IMPORT_SECRETS(destination).syncId)
|
||||
}),
|
||||
querystring: z.object({
|
||||
importBehavior: z
|
||||
.nativeEnum(SecretSyncImportBehavior)
|
||||
.describe(SecretSyncs.IMPORT_SECRETS(destination).importBehavior)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
const { importBehavior } = req.query;
|
||||
|
||||
const secretSync = (await server.services.secretSync.triggerSecretSyncImportSecretsById(
|
||||
{
|
||||
syncId,
|
||||
destination,
|
||||
importBehavior
|
||||
},
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:syncId/remove-secrets",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: `Remove previously synced secrets from the specified ${destinationName} Sync destination.`,
|
||||
params: z.object({
|
||||
syncId: z.string().uuid().describe(SecretSyncs.REMOVE_SECRETS(destination).syncId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSync: responseSchema })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { syncId } = req.params;
|
||||
|
||||
const secretSync = (await server.services.secretSync.triggerSecretSyncRemoveSecretsById(
|
||||
{
|
||||
syncId,
|
||||
destination
|
||||
},
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
return { secretSync };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import {
|
||||
AwsParameterStoreSyncListItemSchema,
|
||||
AwsParameterStoreSyncSchema
|
||||
} from "@app/services/secret-sync/aws-parameter-store";
|
||||
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||
|
||||
const SecretSyncSchema = z.discriminatedUnion("destination", [AwsParameterStoreSyncSchema, GitHubSyncSchema]);
|
||||
|
||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
AwsParameterStoreSyncListItemSchema,
|
||||
GitHubSyncListItemSchema
|
||||
]);
|
||||
|
||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/options",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List the available Secret Sync Options.",
|
||||
response: {
|
||||
200: z.object({
|
||||
secretSyncOptions: SecretSyncOptionsSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: () => {
|
||||
const secretSyncOptions = server.services.secretSync.listSecretSyncOptions();
|
||||
return { secretSyncOptions };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "List all the Secret Syncs for the specified project.",
|
||||
querystring: z.object({
|
||||
projectId: z.string().trim().min(1, "Project ID required").describe(SecretSyncs.LIST().projectId)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({ secretSyncs: SecretSyncSchema.array() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const {
|
||||
query: { projectId },
|
||||
permission
|
||||
} = req;
|
||||
|
||||
const secretSyncs = await server.services.secretSync.listSecretSyncsByProjectId({ projectId }, permission);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_SYNCS,
|
||||
metadata: {
|
||||
syncIds: secretSyncs.map((sync) => sync.id),
|
||||
count: secretSyncs.length
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { secretSyncs };
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user