diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 70ba7e87b..58931628e 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -479,3 +479,74 @@ export const PROJECT_USER_ADDITIONAL_PRIVILEGE = { projectMembershipId: "Project membership id of user" } }; + +export const INTEGRATION_AUTH = { + GET: { + integrationAuthId: "The id of integration authentication object." + }, + DELETE: { + integration: "The slug of the integration to be unauthorized.", + projectId: "The ID of the project to delete the integration auth from." + }, + DELETE_BY_ID: { + integrationAuthId: "The id of integration authentication object to delete." + }, + CREATE_ACCESS_TOKEN: { + workspaceId: "The ID of the project to create the integration auth for.", + integration: "The slug of integration for the auth object.", + accessId: "The unique authorized access id of the external integration provider.", + accessToken: "The unique authorized access token of the external integration provider.", + url: "", + namespace: "", + refreshToken: "The refresh token for integration authorization." + }, + LIST_AUTHORIZATION: { + workspaceId: "The ID of the project to list integration auths for." + } +}; + +export const INTEGRATION = { + CREATE: { + integrationAuthId: "The ID of the integration auth object to link with integration.", + app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.", + isActive: "Whether the integration should be active or disabled.", + appId: + "The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.", + secretPath: "The path of the secrets to sync secrets from.", + sourceEnvironment: "The environment to sync secret from.", + targetEnvironment: + "The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.", + targetEnvironmentId: + "The target environment id of the integration provider. Used in cloudflare pages, teamcity, gitlab integrations.", + targetService: + "The service based grouping identifier of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank", + targetServiceId: + "The service based grouping identifier ID of the external provider. Used in Terraform cloud, Checkly, Railway and NorthFlank", + owner: "External integration providers service entity owner. Used in Github.", + path: "Path to save the synced secrets. Used by Gitlab, AWS Parameter Store, Vault", + region: "AWS region to sync secrets to.", + scope: "Scope of the provider. Used by Github, Qovery", + metadata: { + secretPrefix: "The prefix for the saved secret. Used by GCP", + secretSuffix: "The suffix for the saved secret. Used by GCP", + initialSyncBehavoir: "Type of syncing behavoir with the integration", + shouldAutoRedeploy: "Used by Render to trigger auto deploy", + secretGCPLabel: "The label for the GCP secrets" + } + }, + UPDATE: { + integrationId: "The ID of the integration object.", + app: "The name of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.", + appId: + "The ID of the external integration providers app entity that you want to sync secrets with. Used in Netlify, GitHub, Vercel integrations.", + isActive: "Whether the integration should be active or disabled.", + secretPath: "The path of the secrets to sync secrets from.", + owner: "External integration providers service entity owner. Used in Github.", + targetEnvironment: + "The target environment of the integration provider. Used in cloudflare pages, TeamCity, Gitlab integrations.", + environment: "The environment to sync secrets from." + }, + DELETE: { + integrationId: "The ID of the integration object." + } +}; diff --git a/backend/src/server/routes/v1/integration-auth-router.ts b/backend/src/server/routes/v1/integration-auth-router.ts index 004ca298d..a68479074 100644 --- a/backend/src/server/routes/v1/integration-auth-router.ts +++ b/backend/src/server/routes/v1/integration-auth-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { INTEGRATION_AUTH } from "@app/lib/api-docs"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -10,8 +11,14 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) server.route({ url: "/integration-options", method: "GET", - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "List of integrations available.", + security: [ + { + bearerAuth: [] + } + ], response: { 200: z.object({ integrationOptions: z @@ -38,10 +45,16 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) server.route({ url: "/:integrationAuthId", method: "GET", - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Get details of an integration authorization by auth object id.", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ - integrationAuthId: z.string().trim() + integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.GET.integrationAuthId) }), response: { 200: z.object({ @@ -64,11 +77,17 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) server.route({ url: "/", method: "DELETE", - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Remove all integration's auth object from the project.", + security: [ + { + bearerAuth: [] + } + ], querystring: z.object({ - integration: z.string().trim(), - projectId: z.string().trim() + integration: z.string().trim().describe(INTEGRATION_AUTH.DELETE.integration), + projectId: z.string().trim().describe(INTEGRATION_AUTH.DELETE.projectId) }), response: { 200: z.object({ @@ -104,10 +123,16 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) server.route({ url: "/:integrationAuthId", method: "DELETE", - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Remove an integration auth object by object id.", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ - integrationAuthId: z.string().trim() + integrationAuthId: z.string().trim().describe(INTEGRATION_AUTH.DELETE_BY_ID.integrationAuthId) }), response: { 200: z.object({ @@ -183,16 +208,22 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) server.route({ url: "/access-token", method: "POST", - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), schema: { + description: "Create the integration authentication object required for syncing secrets.", + security: [ + { + bearerAuth: [] + } + ], body: z.object({ - workspaceId: z.string().trim(), - integration: z.string().trim(), - accessId: z.string().trim().optional(), - accessToken: z.string().trim().optional(), - url: z.string().url().trim().optional(), - namespace: z.string().trim().optional(), - refreshToken: z.string().trim().optional() + workspaceId: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.workspaceId), + integration: z.string().trim().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.integration), + accessId: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessId), + accessToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.accessToken), + url: z.string().url().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.url), + namespace: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.namespace), + refreshToken: z.string().trim().optional().describe(INTEGRATION_AUTH.CREATE_ACCESS_TOKEN.refreshToken) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 4859ca584..0bc434dbe 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { INTEGRATION } from "@app/lib/api-docs"; import { removeTrailingSlash, shake } from "@app/lib/fn"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -13,33 +14,45 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { url: "/", method: "POST", schema: { + description: "Create an integration to sync secrets.", + security: [ + { + bearerAuth: [] + } + ], body: z.object({ - integrationAuthId: z.string().trim(), - app: z.string().trim().optional(), - isActive: z.boolean(), - appId: z.string().trim().optional(), - secretPath: z.string().trim().default("/").transform(removeTrailingSlash), - sourceEnvironment: z.string().trim(), - targetEnvironment: z.string().trim().optional(), - targetEnvironmentId: z.string().trim().optional(), - targetService: z.string().trim().optional(), - targetServiceId: z.string().trim().optional(), - owner: z.string().trim().optional(), - path: z.string().trim().optional(), - region: z.string().trim().optional(), - scope: z.string().trim().optional(), + integrationAuthId: z.string().trim().describe(INTEGRATION.CREATE.integrationAuthId), + app: z.string().trim().optional().describe(INTEGRATION.CREATE.app), + isActive: z.boolean().describe(INTEGRATION.CREATE.isActive).default(true), + appId: z.string().trim().optional().describe(INTEGRATION.CREATE.appId), + secretPath: z + .string() + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(INTEGRATION.CREATE.secretPath), + sourceEnvironment: z.string().trim().describe(INTEGRATION.CREATE.sourceEnvironment), + targetEnvironment: z.string().trim().optional().describe(INTEGRATION.CREATE.targetEnvironment), + targetEnvironmentId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetEnvironmentId), + targetService: z.string().trim().optional().describe(INTEGRATION.CREATE.targetService), + targetServiceId: z.string().trim().optional().describe(INTEGRATION.CREATE.targetServiceId), + owner: z.string().trim().optional().describe(INTEGRATION.CREATE.owner), + path: z.string().trim().optional().describe(INTEGRATION.CREATE.path), + region: z.string().trim().optional().describe(INTEGRATION.CREATE.region), + scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope), metadata: z .object({ - secretPrefix: z.string().optional(), - secretSuffix: z.string().optional(), - initialSyncBehavior: z.string().optional(), - shouldAutoRedeploy: z.boolean().optional(), + secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix), + secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix), + initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir), + shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy), secretGCPLabel: z .object({ labelName: z.string(), labelValue: z.string() }) .optional() + .describe(INTEGRATION.CREATE.metadata.secretGCPLabel) }) .optional() }), @@ -49,7 +62,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { integration, integrationAuth } = await server.services.integration.createIntegration({ actorId: req.permission.id, @@ -102,17 +115,28 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { url: "/:integrationId", method: "PATCH", schema: { + description: "Update an integration by integration id", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ - integrationId: z.string().trim() + integrationId: z.string().trim().describe(INTEGRATION.UPDATE.integrationId) }), body: z.object({ - app: z.string().trim(), - appId: z.string().trim(), - isActive: z.boolean(), - secretPath: z.string().trim().default("/").transform(removeTrailingSlash), - targetEnvironment: z.string().trim(), - owner: z.string().trim(), - environment: z.string().trim() + app: z.string().trim().describe(INTEGRATION.UPDATE.app), + appId: z.string().trim().describe(INTEGRATION.UPDATE.appId), + isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive), + secretPath: z + .string() + .trim() + .default("/") + .transform(removeTrailingSlash) + .describe(INTEGRATION.UPDATE.secretPath), + targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment), + owner: z.string().trim().describe(INTEGRATION.UPDATE.owner), + environment: z.string().trim().describe(INTEGRATION.UPDATE.environment) }), response: { 200: z.object({ @@ -120,7 +144,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const integration = await server.services.integration.updateIntegration({ actorId: req.permission.id, @@ -138,8 +162,14 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { url: "/:integrationId", method: "DELETE", schema: { + description: "Remove an integration using the integration object ID", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ - integrationId: z.string().trim() + integrationId: z.string().trim().describe(INTEGRATION.DELETE.integrationId) }), response: { 200: z.object({ @@ -147,7 +177,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const integration = await server.services.integration.deleteIntegration({ actorId: req.permission.id, diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 55f1fdda9..ca01820a9 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -7,7 +7,7 @@ import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; -import { PROJECTS } from "@app/lib/api-docs"; +import { INTEGRATION_AUTH, PROJECTS } from "@app/lib/api-docs"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { ProjectFilterType } from "@app/services/project/project-types"; @@ -332,8 +332,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { url: "/:workspaceId/authorizations", method: "GET", schema: { + description: "List integration auth objects for a workspace.", + security: [ + { + bearerAuth: [] + } + ], params: z.object({ - workspaceId: z.string().trim() + workspaceId: z.string().trim().describe(INTEGRATION_AUTH.LIST_AUTHORIZATION.workspaceId) }), response: { 200: z.object({ @@ -341,7 +347,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({ actorId: req.permission.id, diff --git a/docs/api-reference/endpoints/integrations/create-auth.mdx b/docs/api-reference/endpoints/integrations/create-auth.mdx new file mode 100644 index 000000000..5af7a0f9c --- /dev/null +++ b/docs/api-reference/endpoints/integrations/create-auth.mdx @@ -0,0 +1,32 @@ +--- +title: "Create Auth" +openapi: "POST /api/v1/integration-auth/access-token" +--- + +## Integration Authentication Parameters + +The integration authentication endpoint is generic and can be used for all native integrations. +For specific integration parameters for a given service, please review the respective documentation below. + + + + + This value must be **aws-secret-manager**. + + + Infisical project id for the integration. + + + The AWS IAM User Access ID. + + + The AWS IAM User Access Secret Key. + + + + Coming Soon + + + Coming Soon + + diff --git a/docs/api-reference/endpoints/integrations/create.mdx b/docs/api-reference/endpoints/integrations/create.mdx new file mode 100644 index 000000000..0992e91b9 --- /dev/null +++ b/docs/api-reference/endpoints/integrations/create.mdx @@ -0,0 +1,40 @@ +--- +title: "Create" +openapi: "POST /api/v1/integration" +--- + +## Integration Parameters + +The integration creation endpoint is generic and can be used for all native integrations. +For specific integration parameters for a given service, please review the respective documentation below. + + + + + The ID of the integration auth object for authentication with AWS. + Refer [Create Integration Auth](./create-auth) for more info + + + Whether the integration should be active or inactive + + + The secret name used when saving secret in AWS SSM. Used for naming and can be arbitrary. + + + The AWS region of the SSM. Example: `us-east-1` + + + The Infisical environment slug from where secrets will be synced from. Example: `dev` + + + The Infisical folder path from where secrets will be synced from. Example: `/some/path`. The root of the environment is `/`. + + + + Coming Soon + + + Coming Soon + + + diff --git a/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx b/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx new file mode 100644 index 000000000..5884363fc --- /dev/null +++ b/docs/api-reference/endpoints/integrations/delete-auth-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete Auth By ID" +openapi: "DELETE /api/v1/integration-auth/{integrationAuthId}" +--- diff --git a/docs/api-reference/endpoints/integrations/delete-auth.mdx b/docs/api-reference/endpoints/integrations/delete-auth.mdx new file mode 100644 index 000000000..93d957903 --- /dev/null +++ b/docs/api-reference/endpoints/integrations/delete-auth.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete Auth" +openapi: "DELETE /api/v1/integration-auth" +--- diff --git a/docs/api-reference/endpoints/integrations/delete.mdx b/docs/api-reference/endpoints/integrations/delete.mdx new file mode 100644 index 000000000..51df56de7 --- /dev/null +++ b/docs/api-reference/endpoints/integrations/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v1/integration/{integrationId}" +--- diff --git a/docs/api-reference/endpoints/integrations/find-auth.mdx b/docs/api-reference/endpoints/integrations/find-auth.mdx new file mode 100644 index 000000000..439b82935 --- /dev/null +++ b/docs/api-reference/endpoints/integrations/find-auth.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Auth By ID" +openapi: "GET /api/v1/integration-auth/{integrationAuthId}" +--- diff --git a/docs/api-reference/endpoints/integrations/list-auth.mdx b/docs/api-reference/endpoints/integrations/list-auth.mdx new file mode 100644 index 000000000..3ca961d98 --- /dev/null +++ b/docs/api-reference/endpoints/integrations/list-auth.mdx @@ -0,0 +1,4 @@ +--- +title: "List Auth" +openapi: "GET /api/v1/workspace/{workspaceId}/authorizations" +--- diff --git a/docs/api-reference/endpoints/integrations/update.mdx b/docs/api-reference/endpoints/integrations/update.mdx new file mode 100644 index 000000000..8567c46ae --- /dev/null +++ b/docs/api-reference/endpoints/integrations/update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update" +openapi: "PATCH /api/v1/integration/{integrationId}" +--- diff --git a/docs/mint.json b/docs/mint.json index 9d9759f0e..4f70fb4c4 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1,6 +1,6 @@ { "name": "Infisical", - "openapi": "https://app.infisical.com/api/docs/json", + "openapi": "http://localhost:8080/api/docs/json", "logo": { "dark": "/logo/dark.svg", "light": "/logo/light.svg", @@ -32,7 +32,10 @@ "thumbsRating": true }, "api": { - "baseUrl": ["https://app.infisical.com", "http://localhost:8080"] + "baseUrl": [ + "https://app.infisical.com", + "http://localhost:8080" + ] }, "topbarLinks": [ { @@ -279,7 +282,7 @@ "integrations/cloud/azure-key-vault", "integrations/cloud/gcp-secret-manager", { - "group": "Cloudflare", + "group": "Cloudflare", "pages": [ "integrations/cloud/cloudflare-pages", "integrations/cloud/cloudflare-workers" @@ -356,14 +359,16 @@ }, { "group": "Build Tool Integrations", - "pages": ["integrations/build-tools/gradle"] + "pages": [ + "integrations/build-tools/gradle" + ] }, { "group": "", "pages": [ "sdks/overview" ] - }, + }, { "group": "SDK's", "pages": [ @@ -494,13 +499,30 @@ "api-reference/endpoints/secret-imports/delete" ] }, + { + "group": "Integrations", + "pages": [ + "api-reference/endpoints/integrations/create-auth", + "api-reference/endpoints/integrations/list-auth", + "api-reference/endpoints/integrations/find-auth", + "api-reference/endpoints/integrations/delete-auth", + "api-reference/endpoints/integrations/delete-auth-by-id", + "api-reference/endpoints/integrations/create", + "api-reference/endpoints/integrations/update", + "api-reference/endpoints/integrations/delete" + ] + }, { "group": "Service Tokens", - "pages": ["api-reference/endpoints/service-tokens/get"] + "pages": [ + "api-reference/endpoints/service-tokens/get" + ] }, { "group": "Audit Logs", - "pages": ["api-reference/endpoints/audit-logs/export-audit-log"] + "pages": [ + "api-reference/endpoints/audit-logs/export-audit-log" + ] } ] }, @@ -516,21 +538,22 @@ }, { "group": "", - "pages": ["changelog/overview"] + "pages": [ + "changelog/overview" + ] }, { "group": "Contributing", "pages": [ - { - "group": "Getting Started", - "pages": [ - "contributing/getting-started/overview", - "contributing/getting-started/code-of-conduct", - "contributing/getting-started/pull-requests", - "contributing/getting-started/faq" - - ] - }, + { + "group": "Getting Started", + "pages": [ + "contributing/getting-started/overview", + "contributing/getting-started/code-of-conduct", + "contributing/getting-started/pull-requests", + "contributing/getting-started/faq" + ] + }, { "group": "Contributing to platform", "pages": [ @@ -540,11 +563,11 @@ ] }, { - "group": "Contributing to SDK", - "pages": [ - "contributing/sdk/developing" - ] - } + "group": "Contributing to SDK", + "pages": [ + "contributing/sdk/developing" + ] + } ] } ],