feat: v6.7.0 — SSO/SAML + OIDC entreprise (SP SAML signé + OIDC PKCE, auto-provisioning, groupes IdP → rôles workspace, SSO only, onglet Settings « SSO / Enterprise », API /api/v2/sso/*, help, migration 23, docs + OpenAPI 439 chemins) · 802 tests verts
This commit is contained in:
+38
-2
@@ -7,6 +7,10 @@
|
||||
> marketplace de skills (`/api/v2/skills/*`) dans `app/routers/api_v2_agent.py`, logique
|
||||
> partagée `app/services/skill_gallery.py`, OpenAPI régénéré (**427 chemins**), 15 tests dédiés
|
||||
> (`tests/test_v66_agent_api.py`). Voir §2.4.
|
||||
> **v6.7.0 (2026-09-24)** — **SSO / SAML + OIDC entreprise** : parcours navigateur
|
||||
> `/auth/saml/*` + `/auth/oidc/*` (hors scope `/api/v2`), API admin de configuration
|
||||
> `/api/v2/sso/*` (session + admin + CSRF), logique partagée `app/services/sso_provisioning.py`,
|
||||
> OpenAPI régénéré, 38 tests dédiés (`tests/test_v67_sso.py`). Voir §2.5.
|
||||
> Les sections ci-dessous décrivent les conventions cibles et restent la référence de conception.
|
||||
> **Dernière mise à jour** : 2026-09-24
|
||||
> **Portée** : inventaire de l'API existante, conventions cibles, design CRUD par ressource, webhooks, sécurité, checklist d'implémentation.
|
||||
@@ -259,9 +263,41 @@ Mêmes endpoints (session cookie) côté interne : `/api/agent/skills/gallery`,
|
||||
(catalogue `app/services/webhook_outbound.py`, abonnement `agent.*` possible).
|
||||
4. Chaque mutation écrit `api_audit_log` (`agent.create`, `agent.run`, `skill.import`, …).
|
||||
|
||||
---
|
||||
### 2.5 SSO / Enterprise auth (`app/routers/sso.py`, v6.7.0)
|
||||
|
||||
## 3. Conventions cibles pour l'API v2
|
||||
> **Authentification fédérée** : un IdP d'entreprise (SAML 2.0 ou OpenID Connect + PKCE)
|
||||
> ouvre une session FlowDeck ; les comptes sont créés au premier login et les groupes de
|
||||
> l'IdP deviennent des rôles workspace. Design : `docs/V6_SSO_SAML_Enterprise_Auth.md`.
|
||||
|
||||
**Parcours navigateur (session cookie, CSRF exclu — POST IdP cross-site)**
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | `/auth/saml/login` | Redirection SP-initiée vers l'IdP (`RelayState` = next sûr) |
|
||||
| POST | `/auth/saml/callback` | Assertion Consumer Service — signature/aud/dest/InResponseTo validés |
|
||||
| GET | `/auth/saml/metadata` | Métadonnées SP XML (EntityID, ACS, SLO, certificat) |
|
||||
| GET | `/auth/saml/logout` | SLO — relaye `SAMLRequest` à l'IdP puis détruit la session (POST accepté pour l'IdP) |
|
||||
| GET | `/auth/oidc/login` | Redirection authorize (`state` + `code_verifier` en DB) |
|
||||
| GET | `/auth/oidc/callback` | Code → token → userinfo ; ID token vérifié (JWKS, aud/iss/nonce/exp) |
|
||||
| GET/POST | `/auth/oidc/logout` | Déconnexion OIDC (relaye à l'IdP si `end_session_endpoint`) |
|
||||
|
||||
**API admin (session + rôle admin + header `X-CSRF-Token`)**
|
||||
|
||||
| Méthode | Route | Description |
|
||||
|---------|-------|-------------|
|
||||
| GET | `/api/v2/sso/providers` | **Public** (page de login) : `{providers[{type,name,icon,login_url}], sso_only, base_url}` |
|
||||
| GET | `/api/v2/sso/config` | Config publique (`{configured, source: db\|env, client_secret_set, provisioned_users}` — **jamais** le secret) |
|
||||
| POST/PUT | `/api/v2/sso/config` | Crée/met à jour (`{configured:false}` = actif) ; champ secret vide = conserver |
|
||||
| DELETE | `/api/v2/sso/config` | Désactive le SSO (les comptes SSO existants restent) |
|
||||
| GET | `/api/v2/sso/workspaces` | Épingles pour le mapping (workspaces + `provisioned_users`) |
|
||||
| POST | `/api/v2/sso/sync` | Re-synchronise les groupes de tous les utilisateurs SSO |
|
||||
| GET | `/api/v2/sso/history` | Journal d'audit des tentatives (`limit` ≤ 200) |
|
||||
|
||||
Règles : secrets chiffrés Fernet (`sso_config`), anti-replay `sso_requests` (TTL 15 min),
|
||||
historique `sso_login_history` (succès/échecs), mode SSO only (login local refusé sauf admins),
|
||||
bootstrap possible par variables `SSO_*` du `.env` (la config admin prime).
|
||||
|
||||
---
|
||||
|
||||
### 3.1 Auth & tokens
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# V6.0.0 — SSO / SAML : Enterprise Authentication
|
||||
|
||||
> **Statut** : Conception détaillée — v6.0.0
|
||||
> **Statut** : ✅ **Livré en v6.7.0** (2026-09-24) — conception historique ci-dessous
|
||||
> **Date** : 2026-09-15
|
||||
> **Route** : `feat/v6-sso-saml` → `develop` → `main`
|
||||
> **Dépendances** : v4.0.0 Accounts & Integrations (OAuth2 Gitea/GitHub existant)
|
||||
@@ -181,7 +181,7 @@ SSO_PROVIDER=saml
|
||||
SSO_ENTITY_ID=https://sts.windows.net/{tenant-id}/
|
||||
SSO_SSO_URL=https://login.microsoftonline.com/{tenant-id}/saml2
|
||||
SSO_SLO_URL=https://login.microsoftonline.com/{tenant-id}/saml2/logout
|
||||
SSO_X509_CERT="-----BEGIN CERTIFICATE-----\n..."
|
||||
SSO_X509_CERTIFICATE="-----BEGIN CERTIFICATE-----\n..."
|
||||
SSO_ATTRIBUTE_MAPPING={"login":"nameid","email":"email","full_name":"name"}
|
||||
SSO_AUTO_PROVISION=true
|
||||
SSO_DEFAULT_WORKSPACE_ID=1
|
||||
@@ -432,18 +432,20 @@ ALTER TABLE users ADD COLUMN auth_method TEXT DEFAULT 'local';
|
||||
|
||||
## 9. Checklist d'implémentation
|
||||
|
||||
1. **`auth/providers/saml_provider.py`** — wrapper python3-saml ou pysaml2
|
||||
2. **`auth/providers/oidc_provider.py`** — wrapper OIDC (authlib ou httpx)
|
||||
3. **`auth/routers/sso.py`** — endpoints SSO/OIDC
|
||||
4. **Migration `sso_config`** + `sso_login_history` + colonne `auth_method` sur `users`
|
||||
5. **`services/sso_provisioning.py`** — auto-provision + group mapping
|
||||
6. **Extension `settings.html`** — UI admin SSO
|
||||
7. **Extension `login.html`** — boutons SSO
|
||||
8. **SP metadata endpoint** (`/auth/saml/metadata`)
|
||||
9. **Security** — validation assertions, rate limiting, audit log
|
||||
10. **Tests** — tous les scénarios SSO
|
||||
11. **Documentation utilisateur** — `/help` section SSO setup
|
||||
12. **Dépendance** — `python3-saml` ou `pysaml2`, `authlib` dans requirements.txt
|
||||
> ✅ **Tout est livré en v6.7.0** (`tests/test_v67_sso.py`, 38 tests) :
|
||||
|
||||
1. ✅ **`auth/providers/saml_provider.py`** — wrapper python3-saml (`app/auth/providers/saml_provider.py`)
|
||||
2. ✅ **`auth/providers/oidc_provider.py`** — wrapper OIDC authlib (`app/auth/providers/oidc_provider.py`)
|
||||
3. ✅ **`routers/sso.py`** — endpoints SSO/OIDC (`app/routers/sso.py` + API admin `/api/v2/sso/*`)
|
||||
4. ✅ **Migration 23 `sso_config`** + `sso_login_history` + `sso_requests` + colonne `auth_method` sur `users` (déjà présente)
|
||||
5. ✅ **`services/sso_provisioning.py`** — auto-provision + group mapping
|
||||
6. ✅ **Extension `settings.html`** — UI admin SSO (onglet « SSO / Enterprise »)
|
||||
7. ✅ **Extension page de login** — boutons SSO (nom dynamique)
|
||||
8. ✅ **SP metadata endpoint** (`/auth/saml/metadata`)
|
||||
9. ✅ **Security** — validation assertions (sign/aud/dest/InResponseTo), anti-replay `sso_requests`, rate limit login, historique
|
||||
10. ✅ **Tests** — `tests/test_v67_sso.py` : 38 scénarios (flots SAML/OIDC, négatifs, groupes, sso_only, SLO)
|
||||
11. ✅ **Documentation utilisateur** — `/help` section « SSO (Enterprise) »
|
||||
12. ✅ **Dépendance** — `python3-saml==1.16.0`, `authlib==1.8.0`, `cryptography>=42.0` dans requirements.txt
|
||||
|
||||
---
|
||||
|
||||
|
||||
+504
-2
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FlowDeck",
|
||||
"version": "6.6.0"
|
||||
"version": "6.7.0"
|
||||
},
|
||||
"paths": {
|
||||
"/auth/register": {
|
||||
@@ -161,7 +161,7 @@
|
||||
"auth"
|
||||
],
|
||||
"summary": "Logout",
|
||||
"description": "Clear session and redirect to login page.",
|
||||
"description": "Clear session and redirect to login page.\n\nSAML sessions additionally hand over to the IdP's Single Logout when one\nis configured (the actual cookie clearing happens on the SLO route).",
|
||||
"operationId": "logout_auth_logout_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
@@ -195,6 +195,508 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/saml/login": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Saml Login",
|
||||
"description": "SP-initiated SSO: issue an AuthnRequest and redirect to the IdP.",
|
||||
"operationId": "saml_login_auth_saml_login_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/workspaces",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/saml/callback": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Saml Callback",
|
||||
"description": "Assertion Consumer Service — validate the SAMLResponse and open a session.",
|
||||
"operationId": "saml_callback_auth_saml_callback_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/saml/metadata": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Saml Metadata",
|
||||
"description": "SP metadata XML — paste into the IdP (Azure AD / Okta / Keycloak…).",
|
||||
"operationId": "saml_metadata_auth_saml_metadata_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/saml/logout": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Saml Logout",
|
||||
"description": "SP-initiated Single Logout (GET) — hands the browser to the IdP.",
|
||||
"operationId": "saml_logout_auth_saml_logout_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/auth/login?provider=local",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Saml Logout Post",
|
||||
"description": "IdP-initiated Single Logout (POST with SAMLRequest/SAMLResponse).",
|
||||
"operationId": "saml_logout_post_auth_saml_logout_post",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/auth/login?provider=local",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/oidc/login": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Oidc Login",
|
||||
"description": "Redirect to the OIDC provider (authorization code + PKCE).",
|
||||
"operationId": "oidc_login_auth_oidc_login_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/workspaces",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/oidc/callback": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Oidc Callback",
|
||||
"description": "OIDC callback (GET, authorization code in the query string).",
|
||||
"operationId": "oidc_callback_auth_oidc_callback_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Oidc Callback Post",
|
||||
"description": "OIDC callback (POST, form_post response mode).",
|
||||
"operationId": "oidc_callback_post_auth_oidc_callback_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/oidc/logout": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Oidc Logout",
|
||||
"description": "OIDC logout (GET) — local session first, then the IdP end-session URL.",
|
||||
"operationId": "oidc_logout_auth_oidc_logout_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/auth/login?provider=local",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Oidc Logout Post",
|
||||
"description": "OIDC logout (POST).",
|
||||
"operationId": "oidc_logout_post_auth_oidc_logout_post",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "next",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "/auth/login?provider=local",
|
||||
"title": "Next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/sso/providers": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Sso Providers",
|
||||
"description": "Public: what the login page should show (button list + sso_only flag).",
|
||||
"operationId": "sso_providers_api_v2_sso_providers_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/sso/config": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Get Sso Config Api",
|
||||
"description": "Read the current SSO configuration (secrets never returned).",
|
||||
"operationId": "get_sso_config_api_api_v2_sso_config_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Save Sso Config Api",
|
||||
"description": "Create/replace the SSO configuration (admin, scope write).",
|
||||
"operationId": "save_sso_config_api_api_v2_sso_config_put",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Save Sso Config Api",
|
||||
"description": "Create/replace the SSO configuration (admin, scope write).",
|
||||
"operationId": "save_sso_config_api_api_v2_sso_config_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Delete Sso Config Api",
|
||||
"description": "Disable SSO — local logins keep working (design §8 « SSO disable »).",
|
||||
"operationId": "delete_sso_config_api_api_v2_sso_config_delete",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/sso/workspaces": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Sso Workspaces",
|
||||
"description": "Workspaces available for default assignment / group mapping.",
|
||||
"operationId": "sso_workspaces_api_v2_sso_workspaces_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/sso/sync": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Sso Sync",
|
||||
"description": "Re-apply group → workspace role mapping for every SSO user.",
|
||||
"operationId": "sso_sync_api_v2_sso_sync_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/sso/history": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sso"
|
||||
],
|
||||
"summary": "Sso History",
|
||||
"description": "Audit trail of SSO login attempts (successes and rejections).",
|
||||
"operationId": "sso_history_api_v2_sso_history_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"default": 50,
|
||||
"title": "Limit"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/trash": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user