Skip to content

Commit

Permalink
feat(engine): Add SAML SSO endpoints (#447)
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Lo <[email protected]>
Co-authored-by: Daryl Lim <[email protected]>
  • Loading branch information
topher-lo and daryllimyt authored Oct 25, 2024
1 parent 1695c38 commit 6adb896
Show file tree
Hide file tree
Showing 23 changed files with 581 additions and 16 deletions.
14 changes: 13 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ COMPOSE_PROJECT_NAME=tracecat
# --- Shared URL env vars ---
PUBLIC_APP_URL=http://localhost
PUBLIC_API_URL=http://localhost/api
SAML_SP_ACS_URL=${PUBLIC_API_URL}/auth/saml/acs
INTERNAL_API_URL=http://api:8000

# -- Caddy env vars ---
Expand Down Expand Up @@ -61,13 +62,24 @@ NEXT_PUBLIC_API_URL=${PUBLIC_API_URL}
NEXT_SERVER_API_URL=${INTERNAL_API_URL}

# --- Authentication ---
# One or more comma-separated values from `basic`, `google_oauth`

# One or more comma-separated values from `basic`, `google_oauth`, `saml`
TRACECAT__AUTH_TYPES=basic,google_oauth

# Initial admin user
TRACECAT__SETUP_ADMIN_EMAIL=
TRACECAT__SETUP_ADMIN_PASSWORD=

# OAuth
OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET=
USER_AUTH_SECRET=your-auth-secret

# SAML SSO settings
SAML_IDP_ENTITY_ID=
SAML_IDP_REDIRECT_URL=
SAML_IDP_CERTIFICATE=
SAML_IDP_METADATA_URL=

# --- Temporal ---
TEMPORAL__CLUSTER_URL=temporal:7233
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ EXPOSE $PORT

# Install necessary packages
RUN apt-get update && \
apt-get install -y acl git && \
apt-get install -y acl git xmlsec1 && \
rm -rf /var/lib/apt/lists/*

# Copy and run the script to install additional packages
Expand Down
3 changes: 3 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ ENV PORT=8000
# Expose the application port
EXPOSE $PORT

# Install xmlsec1
RUN apt-get update && apt-get install -y xmlsec1

# Set the working directory inside the container
WORKDIR /app

Expand Down
11 changes: 10 additions & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ services:
OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID}
OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET}
USER_AUTH_SECRET: ${USER_AUTH_SECRET}
# Initial admin user
TRACECAT__SETUP_ADMIN_EMAIL: ${TRACECAT__SETUP_ADMIN_EMAIL}
TRACECAT__SETUP_ADMIN_PASSWORD: ${TRACECAT__SETUP_ADMIN_PASSWORD}
# SAML SSO
SAML_IDP_ENTITY_ID: ${SAML_IDP_ENTITY_ID}
SAML_IDP_REDIRECT_URL: ${SAML_IDP_REDIRECT_URL}
SAML_IDP_CERTIFICATE: ${SAML_IDP_CERTIFICATE}
SAML_IDP_METADATA_URL: ${SAML_IDP_METADATA_URL}
SAML_SP_ACS_URL: ${SAML_SP_ACS_URL}
# Temporal
TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
Expand Down Expand Up @@ -155,7 +164,7 @@ services:
image: temporalio/ui:${TEMPORAL__UI_VERSION}
container_name: temporal_ui
ports:
- 8080:8080
- 9090:8080
environment:
- TEMPORAL_ADDRESS=temporal:7233
- TEMPORAL_CORS_ORIGINS=http://localhost:3000
Expand Down
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ services:
OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET}
USER_AUTH_SECRET: ${USER_AUTH_SECRET}
RUN_MIGRATIONS: "true"
# Initial admin user
TRACECAT__SETUP_ADMIN_EMAIL: ${TRACECAT__SETUP_ADMIN_EMAIL}
TRACECAT__SETUP_ADMIN_PASSWORD: ${TRACECAT__SETUP_ADMIN_PASSWORD}
# SAML SSO
SAML_IDP_ENTITY_ID: ${SAML_IDP_ENTITY_ID}
SAML_IDP_REDIRECT_URL: ${SAML_IDP_REDIRECT_URL}
SAML_IDP_CERTIFICATE: ${SAML_IDP_CERTIFICATE}
SAML_IDP_METADATA_URL: ${SAML_IDP_METADATA_URL}
SAML_SP_ACS_URL: ${SAML_SP_ACS_URL}
# Temporal
TEMPORAL__CLUSTER_URL: ${TEMPORAL__CLUSTER_URL}
TEMPORAL__CLUSTER_QUEUE: ${TEMPORAL__CLUSTER_QUEUE}
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/app/auth/oauth/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { NextRequest, NextResponse } from "next/server"
import { buildUrl, getDomain } from "@/lib/ss-utils"

/**
* Wrapper around the FastAPI endpoint /auth/oauth/callback,
* which adds back a redirect to the main app.
* @param request
* @returns
*/
Expand Down
51 changes: 51 additions & 0 deletions frontend/src/app/auth/saml/acs/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NextRequest, NextResponse } from "next/server"

import { buildUrl, getDomain } from "@/lib/ss-utils"

/**
* @param request
* @returns
*/
export async function POST(request: NextRequest) {
console.log("POST /auth/saml/acs", request.nextUrl.toString())

// Parse the form data from the request
const formData = await request.formData()
const samlResponse = formData.get('SAMLResponse')

if (!samlResponse) {
console.error("No SAML response found in the request")
return NextResponse.redirect(new URL("/auth/error", getDomain(request)))
}

// Prepare the request to the FastAPI backend
const backendUrl = new URL(buildUrl("/auth/saml/acs"))
const backendFormData = new FormData()
backendFormData.append('SAMLResponse', samlResponse)

// Forward the request to the FastAPI backend
const backendResponse = await fetch(backendUrl.toString(), {
method: 'POST',
body: backendFormData,
})

if (!backendResponse.ok) {
console.error("Error from backend:", await backendResponse.text())
return NextResponse.redirect(new URL("/auth/error", getDomain(request)))
}

const setCookieHeader = backendResponse.headers.get("set-cookie")

if (!setCookieHeader) {
console.error("No set-cookie header found in response")
return NextResponse.redirect(new URL("/auth/error", getDomain(request)))
}

console.log("Redirecting to / with GET")
const redirectUrl = new URL("/", getDomain(request))
const redirectResponse = NextResponse.redirect(redirectUrl, {
status: 303 // Force GET request
})
redirectResponse.headers.set("set-cookie", setCookieHeader)
return redirectResponse
}
24 changes: 24 additions & 0 deletions frontend/src/client/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,18 @@ export const $Body_auth_reset_reset_password = {
title: 'Body_auth-reset:reset_password'
} as const;

export const $Body_auth_sso_acs = {
properties: {
SAMLResponse: {
type: 'string',
title: 'Samlresponse'
}
},
type: 'object',
required: ['SAMLResponse'],
title: 'Body_auth-sso_acs'
} as const;

export const $Body_auth_verify_request_token = {
properties: {
email: {
Expand Down Expand Up @@ -1980,6 +1992,18 @@ export const $RunContext = {
description: 'This is the runtime context model for a workflow run. Passed into activities.'
} as const;

export const $SAMLDatabaseLoginResponse = {
properties: {
redirect_url: {
type: 'string',
title: 'Redirect Url'
}
},
type: 'object',
required: ['redirect_url'],
title: 'SAMLDatabaseLoginResponse'
} as const;

export const $Schedule = {
properties: {
owner_id: {
Expand Down
30 changes: 29 additions & 1 deletion frontend/src/client/services.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { CancelablePromise } from './core/CancelablePromise';
import { OpenAPI } from './core/OpenAPI';
import { request as __request } from './core/request';
import type { PublicIncomingWebhookData, PublicIncomingWebhookResponse, PublicIncomingWebhookWaitData, PublicIncomingWebhookWaitResponse, WorkspacesListWorkspacesResponse, WorkspacesCreateWorkspaceData, WorkspacesCreateWorkspaceResponse, WorkspacesSearchWorkspacesData, WorkspacesSearchWorkspacesResponse, WorkspacesGetWorkspaceData, WorkspacesGetWorkspaceResponse, WorkspacesUpdateWorkspaceData, WorkspacesUpdateWorkspaceResponse, WorkspacesDeleteWorkspaceData, WorkspacesDeleteWorkspaceResponse, WorkspacesListWorkspaceMembershipsData, WorkspacesListWorkspaceMembershipsResponse, WorkspacesCreateWorkspaceMembershipData, WorkspacesCreateWorkspaceMembershipResponse, WorkspacesGetWorkspaceMembershipData, WorkspacesGetWorkspaceMembershipResponse, WorkspacesDeleteWorkspaceMembershipData, WorkspacesDeleteWorkspaceMembershipResponse, WorkflowsListWorkflowsData, WorkflowsListWorkflowsResponse, WorkflowsCreateWorkflowData, WorkflowsCreateWorkflowResponse, WorkflowsGetWorkflowData, WorkflowsGetWorkflowResponse, WorkflowsUpdateWorkflowData, WorkflowsUpdateWorkflowResponse, WorkflowsDeleteWorkflowData, WorkflowsDeleteWorkflowResponse, WorkflowsCommitWorkflowData, WorkflowsCommitWorkflowResponse, WorkflowsExportWorkflowData, WorkflowsExportWorkflowResponse, WorkflowsGetWorkflowDefinitionData, WorkflowsGetWorkflowDefinitionResponse, WorkflowsCreateWorkflowDefinitionData, WorkflowsCreateWorkflowDefinitionResponse, TriggersCreateWebhookData, TriggersCreateWebhookResponse, TriggersGetWebhookData, TriggersGetWebhookResponse, TriggersUpdateWebhookData, TriggersUpdateWebhookResponse, WorkflowExecutionsListWorkflowExecutionsData, WorkflowExecutionsListWorkflowExecutionsResponse, WorkflowExecutionsCreateWorkflowExecutionData, WorkflowExecutionsCreateWorkflowExecutionResponse, WorkflowExecutionsGetWorkflowExecutionData, WorkflowExecutionsGetWorkflowExecutionResponse, WorkflowExecutionsListWorkflowExecutionEventHistoryData, WorkflowExecutionsListWorkflowExecutionEventHistoryResponse, WorkflowExecutionsCancelWorkflowExecutionData, WorkflowExecutionsCancelWorkflowExecutionResponse, WorkflowExecutionsTerminateWorkflowExecutionData, WorkflowExecutionsTerminateWorkflowExecutionResponse, ActionsListActionsData, ActionsListActionsResponse, ActionsCreateActionData, ActionsCreateActionResponse, ActionsGetActionData, ActionsGetActionResponse, ActionsUpdateActionData, ActionsUpdateActionResponse, ActionsDeleteActionData, ActionsDeleteActionResponse, SecretsSearchSecretsData, SecretsSearchSecretsResponse, SecretsListSecretsData, SecretsListSecretsResponse, SecretsCreateSecretData, SecretsCreateSecretResponse, SecretsGetSecretByNameData, SecretsGetSecretByNameResponse, SecretsUpdateSecretByIdData, SecretsUpdateSecretByIdResponse, SecretsDeleteSecretByIdData, SecretsDeleteSecretByIdResponse, SchedulesListSchedulesData, SchedulesListSchedulesResponse, SchedulesCreateScheduleData, SchedulesCreateScheduleResponse, SchedulesGetScheduleData, SchedulesGetScheduleResponse, SchedulesUpdateScheduleData, SchedulesUpdateScheduleResponse, SchedulesDeleteScheduleData, SchedulesDeleteScheduleResponse, SchedulesSearchSchedulesData, SchedulesSearchSchedulesResponse, ValidationValidateWorkflowData, ValidationValidateWorkflowResponse, UsersSearchUserData, UsersSearchUserResponse, RegistryRepositoriesSyncRegistryRepositoriesData, RegistryRepositoriesSyncRegistryRepositoriesResponse, RegistryRepositoriesListRegistryRepositoriesResponse, RegistryRepositoriesCreateRegistryRepositoryData, RegistryRepositoriesCreateRegistryRepositoryResponse, RegistryRepositoriesGetRegistryRepositoryData, RegistryRepositoriesGetRegistryRepositoryResponse, RegistryRepositoriesUpdateRegistryRepositoryData, RegistryRepositoriesUpdateRegistryRepositoryResponse, RegistryRepositoriesDeleteRegistryRepositoryData, RegistryRepositoriesDeleteRegistryRepositoryResponse, RegistryActionsListRegistryActionsResponse, RegistryActionsCreateRegistryActionData, RegistryActionsCreateRegistryActionResponse, RegistryActionsGetRegistryActionData, RegistryActionsGetRegistryActionResponse, RegistryActionsUpdateRegistryActionData, RegistryActionsUpdateRegistryActionResponse, RegistryActionsDeleteRegistryActionData, RegistryActionsDeleteRegistryActionResponse, RegistryActionsRunRegistryActionData, RegistryActionsRunRegistryActionResponse, RegistryActionsValidateRegistryActionData, RegistryActionsValidateRegistryActionResponse, UsersUsersCurrentUserResponse, UsersUsersPatchCurrentUserData, UsersUsersPatchCurrentUserResponse, UsersUsersUserData, UsersUsersUserResponse, UsersUsersPatchUserData, UsersUsersPatchUserResponse, UsersUsersDeleteUserData, UsersUsersDeleteUserResponse, AuthAuthDatabaseLoginData, AuthAuthDatabaseLoginResponse, AuthAuthDatabaseLogoutResponse, AuthRegisterRegisterData, AuthRegisterRegisterResponse, AuthResetForgotPasswordData, AuthResetForgotPasswordResponse, AuthResetResetPasswordData, AuthResetResetPasswordResponse, AuthVerifyRequestTokenData, AuthVerifyRequestTokenResponse, AuthVerifyVerifyData, AuthVerifyVerifyResponse, AuthOauthGoogleDatabaseAuthorizeData, AuthOauthGoogleDatabaseAuthorizeResponse, AuthOauthGoogleDatabaseCallbackData, AuthOauthGoogleDatabaseCallbackResponse, PublicCheckHealthResponse } from './types.gen';
import type { PublicIncomingWebhookData, PublicIncomingWebhookResponse, PublicIncomingWebhookWaitData, PublicIncomingWebhookWaitResponse, WorkspacesListWorkspacesResponse, WorkspacesCreateWorkspaceData, WorkspacesCreateWorkspaceResponse, WorkspacesSearchWorkspacesData, WorkspacesSearchWorkspacesResponse, WorkspacesGetWorkspaceData, WorkspacesGetWorkspaceResponse, WorkspacesUpdateWorkspaceData, WorkspacesUpdateWorkspaceResponse, WorkspacesDeleteWorkspaceData, WorkspacesDeleteWorkspaceResponse, WorkspacesListWorkspaceMembershipsData, WorkspacesListWorkspaceMembershipsResponse, WorkspacesCreateWorkspaceMembershipData, WorkspacesCreateWorkspaceMembershipResponse, WorkspacesGetWorkspaceMembershipData, WorkspacesGetWorkspaceMembershipResponse, WorkspacesDeleteWorkspaceMembershipData, WorkspacesDeleteWorkspaceMembershipResponse, WorkflowsListWorkflowsData, WorkflowsListWorkflowsResponse, WorkflowsCreateWorkflowData, WorkflowsCreateWorkflowResponse, WorkflowsGetWorkflowData, WorkflowsGetWorkflowResponse, WorkflowsUpdateWorkflowData, WorkflowsUpdateWorkflowResponse, WorkflowsDeleteWorkflowData, WorkflowsDeleteWorkflowResponse, WorkflowsCommitWorkflowData, WorkflowsCommitWorkflowResponse, WorkflowsExportWorkflowData, WorkflowsExportWorkflowResponse, WorkflowsGetWorkflowDefinitionData, WorkflowsGetWorkflowDefinitionResponse, WorkflowsCreateWorkflowDefinitionData, WorkflowsCreateWorkflowDefinitionResponse, TriggersCreateWebhookData, TriggersCreateWebhookResponse, TriggersGetWebhookData, TriggersGetWebhookResponse, TriggersUpdateWebhookData, TriggersUpdateWebhookResponse, WorkflowExecutionsListWorkflowExecutionsData, WorkflowExecutionsListWorkflowExecutionsResponse, WorkflowExecutionsCreateWorkflowExecutionData, WorkflowExecutionsCreateWorkflowExecutionResponse, WorkflowExecutionsGetWorkflowExecutionData, WorkflowExecutionsGetWorkflowExecutionResponse, WorkflowExecutionsListWorkflowExecutionEventHistoryData, WorkflowExecutionsListWorkflowExecutionEventHistoryResponse, WorkflowExecutionsCancelWorkflowExecutionData, WorkflowExecutionsCancelWorkflowExecutionResponse, WorkflowExecutionsTerminateWorkflowExecutionData, WorkflowExecutionsTerminateWorkflowExecutionResponse, ActionsListActionsData, ActionsListActionsResponse, ActionsCreateActionData, ActionsCreateActionResponse, ActionsGetActionData, ActionsGetActionResponse, ActionsUpdateActionData, ActionsUpdateActionResponse, ActionsDeleteActionData, ActionsDeleteActionResponse, SecretsSearchSecretsData, SecretsSearchSecretsResponse, SecretsListSecretsData, SecretsListSecretsResponse, SecretsCreateSecretData, SecretsCreateSecretResponse, SecretsGetSecretByNameData, SecretsGetSecretByNameResponse, SecretsUpdateSecretByIdData, SecretsUpdateSecretByIdResponse, SecretsDeleteSecretByIdData, SecretsDeleteSecretByIdResponse, SchedulesListSchedulesData, SchedulesListSchedulesResponse, SchedulesCreateScheduleData, SchedulesCreateScheduleResponse, SchedulesGetScheduleData, SchedulesGetScheduleResponse, SchedulesUpdateScheduleData, SchedulesUpdateScheduleResponse, SchedulesDeleteScheduleData, SchedulesDeleteScheduleResponse, SchedulesSearchSchedulesData, SchedulesSearchSchedulesResponse, ValidationValidateWorkflowData, ValidationValidateWorkflowResponse, UsersSearchUserData, UsersSearchUserResponse, RegistryRepositoriesSyncRegistryRepositoriesData, RegistryRepositoriesSyncRegistryRepositoriesResponse, RegistryRepositoriesListRegistryRepositoriesResponse, RegistryRepositoriesCreateRegistryRepositoryData, RegistryRepositoriesCreateRegistryRepositoryResponse, RegistryRepositoriesGetRegistryRepositoryData, RegistryRepositoriesGetRegistryRepositoryResponse, RegistryRepositoriesUpdateRegistryRepositoryData, RegistryRepositoriesUpdateRegistryRepositoryResponse, RegistryRepositoriesDeleteRegistryRepositoryData, RegistryRepositoriesDeleteRegistryRepositoryResponse, RegistryActionsListRegistryActionsResponse, RegistryActionsCreateRegistryActionData, RegistryActionsCreateRegistryActionResponse, RegistryActionsGetRegistryActionData, RegistryActionsGetRegistryActionResponse, RegistryActionsUpdateRegistryActionData, RegistryActionsUpdateRegistryActionResponse, RegistryActionsDeleteRegistryActionData, RegistryActionsDeleteRegistryActionResponse, RegistryActionsRunRegistryActionData, RegistryActionsRunRegistryActionResponse, RegistryActionsValidateRegistryActionData, RegistryActionsValidateRegistryActionResponse, UsersUsersCurrentUserResponse, UsersUsersPatchCurrentUserData, UsersUsersPatchCurrentUserResponse, UsersUsersUserData, UsersUsersUserResponse, UsersUsersPatchUserData, UsersUsersPatchUserResponse, UsersUsersDeleteUserData, UsersUsersDeleteUserResponse, AuthAuthDatabaseLoginData, AuthAuthDatabaseLoginResponse, AuthAuthDatabaseLogoutResponse, AuthRegisterRegisterData, AuthRegisterRegisterResponse, AuthResetForgotPasswordData, AuthResetForgotPasswordResponse, AuthResetResetPasswordData, AuthResetResetPasswordResponse, AuthVerifyRequestTokenData, AuthVerifyRequestTokenResponse, AuthVerifyVerifyData, AuthVerifyVerifyResponse, AuthOauthGoogleDatabaseAuthorizeData, AuthOauthGoogleDatabaseAuthorizeResponse, AuthOauthGoogleDatabaseCallbackData, AuthOauthGoogleDatabaseCallbackResponse, AuthSamlDatabaseLoginResponse, AuthSsoAcsData, AuthSsoAcsResponse, PublicCheckHealthResponse } from './types.gen';

/**
* Incoming Webhook
Expand Down Expand Up @@ -1639,6 +1639,34 @@ export const authOauthGoogleDatabaseCallback = (data: AuthOauthGoogleDatabaseCal
}
}); };

/**
* Saml:Database.Login
* @returns SAMLDatabaseLoginResponse Successful Response
* @throws ApiError
*/
export const authSamlDatabaseLogin = (): CancelablePromise<AuthSamlDatabaseLoginResponse> => { return __request(OpenAPI, {
method: 'GET',
url: '/auth/saml/login'
}); };

/**
* Sso Acs
* Handle the SAML SSO response from the IdP post-authentication.
* @param data The data for the request.
* @param data.formData
* @returns unknown Successful Response
* @throws ApiError
*/
export const authSsoAcs = (data: AuthSsoAcsData): CancelablePromise<AuthSsoAcsResponse> => { return __request(OpenAPI, {
method: 'POST',
url: '/auth/saml/acs',
formData: data.formData,
mediaType: 'application/x-www-form-urlencoded',
errors: {
422: 'Validation Error'
}
}); };

/**
* Check Health
* @returns string Successful Response
Expand Down
41 changes: 41 additions & 0 deletions frontend/src/client/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ export type Body_auth_reset_reset_password = {
password: string;
};

export type Body_auth_sso_acs = {
SAMLResponse: string;
};

export type Body_auth_verify_request_token = {
email: string;
};
Expand Down Expand Up @@ -741,6 +745,10 @@ export type RunContext = {
environment: string;
};

export type SAMLDatabaseLoginResponse = {
redirect_url: string;
};

export type Schedule = {
owner_id: string;
created_at: string;
Expand Down Expand Up @@ -1765,6 +1773,14 @@ export type AuthOauthGoogleDatabaseCallbackData = {

export type AuthOauthGoogleDatabaseCallbackResponse = unknown;

export type AuthSamlDatabaseLoginResponse = SAMLDatabaseLoginResponse;

export type AuthSsoAcsData = {
formData: Body_auth_sso_acs;
};

export type AuthSsoAcsResponse = unknown;

export type PublicCheckHealthResponse = {
[key: string]: (string);
};
Expand Down Expand Up @@ -2913,6 +2929,31 @@ export type $OpenApiTs = {
};
};
};
'/auth/saml/login': {
get: {
res: {
/**
* Successful Response
*/
200: SAMLDatabaseLoginResponse;
};
};
};
'/auth/saml/acs': {
post: {
req: AuthSsoAcsData;
res: {
/**
* Successful Response
*/
200: unknown;
/**
* Validation Error
*/
422: HTTPValidationError;
};
};
};
'/health': {
get: {
res: {
Expand Down
Loading

0 comments on commit 6adb896

Please sign in to comment.