From ac4124908815fd3ef51df7dcc26491f8e7bec02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Thu, 15 Oct 2026 17:00:27 +0900 Subject: [PATCH 1/5] feat(dgw): explain rejected requests in API error responses API errors now include a machine-readable reason (RFC 9457 problem details). Devolutions Server, Devolutions Hub and other clients can show precise errors, such as a provisioner key mismatch, an expired token (clock skew) or a gateway ID mismatch, instead of a generic failure. Issue: DGW-467 Co-Authored-By: Claude Opus 5.5 --- devolutions-gateway/src/api/config.rs | 8 +- devolutions-gateway/src/api/diagnostics.rs | 7 +- devolutions-gateway/src/api/heartbeat.rs | 6 +- devolutions-gateway/src/api/jrec.rs | 35 +-- devolutions-gateway/src/api/jrl.rs | 16 +- devolutions-gateway/src/api/monitoring.rs | 16 +- devolutions-gateway/src/api/net.rs | 23 +- devolutions-gateway/src/api/preflight.rs | 6 +- devolutions-gateway/src/api/session.rs | 10 +- devolutions-gateway/src/api/sessions.rs | 8 +- devolutions-gateway/src/api/traffic.rs | 18 +- devolutions-gateway/src/api/tunnel.rs | 30 +- devolutions-gateway/src/api/update.rs | 36 +-- devolutions-gateway/src/api/webapp.rs | 2 + devolutions-gateway/src/error_code.rs | 147 ++++++++++ devolutions-gateway/src/extract.rs | 117 ++++++-- devolutions-gateway/src/http.rs | 142 ++++++++- devolutions-gateway/src/lib.rs | 6 +- devolutions-gateway/src/middleware/auth.rs | 11 +- devolutions-gateway/src/openapi.rs | 2 + devolutions-gateway/src/token.rs | 42 +++ docs/API-spec.md | 61 ++++ testsuite/Cargo.toml | 6 +- testsuite/src/dgw_config.rs | 15 +- testsuite/src/lib.rs | 1 + testsuite/src/token.rs | 93 ++++++ testsuite/tests/cli/dgw/api_errors.rs | 323 +++++++++++++++++++++ testsuite/tests/cli/dgw/mod.rs | 1 + testsuite/tests/error_codes.rs | 138 +++++++++ testsuite/tests/main.rs | 1 + 30 files changed, 1179 insertions(+), 148 deletions(-) create mode 100644 devolutions-gateway/src/error_code.rs create mode 100644 testsuite/src/token.rs create mode 100644 testsuite/tests/cli/dgw/api_errors.rs create mode 100644 testsuite/tests/error_codes.rs diff --git a/devolutions-gateway/src/api/config.rs b/devolutions-gateway/src/api/config.rs index 5a4c00c51a..0de039d00e 100644 --- a/devolutions-gateway/src/api/config.rs +++ b/devolutions-gateway/src/api/config.rs @@ -22,10 +22,10 @@ const KEY_ALLOWLIST: &[&str] = &["Id", "SubProvisionerPublicKey", "Subscriber"]; request_body(content = ConfigPatch, description = "JSON-encoded configuration patch", content_type = "application/json"), responses( (status = 200, description = "Configuration has been patched with success"), - (status = 400, description = "Bad patch request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to patch configuration"), + (status = 400, description = "Bad patch request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to patch configuration", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.config.write"])), ))] diff --git a/devolutions-gateway/src/api/diagnostics.rs b/devolutions-gateway/src/api/diagnostics.rs index 2bc44a2735..513c55f393 100644 --- a/devolutions-gateway/src/api/diagnostics.rs +++ b/devolutions-gateway/src/api/diagnostics.rs @@ -111,6 +111,7 @@ impl ClockDiagnostic { path = "/jet/diagnostics/logs", responses( (status = 200, description = "Latest logs", body = String), + // No error body declared: generated clients pick the first JSON media type of all responses, and would expect JSON on success. (status = 400, description = "Bad request"), (status = 401, description = "Invalid or missing authorization token"), (status = 403, description = "Insufficient permissions"), @@ -150,9 +151,9 @@ async fn get_logs( path = "/jet/diagnostics/configuration", responses( (status = 200, description = "Service configuration diagnostic (including version)", body = ConfigDiagnostic), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.diagnostics.read"])), ))] diff --git a/devolutions-gateway/src/api/heartbeat.rs b/devolutions-gateway/src/api/heartbeat.rs index 19cf2c52a8..bfd47e757f 100644 --- a/devolutions-gateway/src/api/heartbeat.rs +++ b/devolutions-gateway/src/api/heartbeat.rs @@ -104,9 +104,9 @@ impl NoDiskState { path = "/jet/heartbeat", responses( (status = 200, description = "Heartbeat for this Gateway", body = Heartbeat), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.heartbeat.read"])), ))] diff --git a/devolutions-gateway/src/api/jrec.rs b/devolutions-gateway/src/api/jrec.rs index 508d32ff5e..45df0a4b56 100644 --- a/devolutions-gateway/src/api/jrec.rs +++ b/devolutions-gateway/src/api/jrec.rs @@ -208,11 +208,11 @@ const STORAGE_FULL_CLOSE_CODE: u16 = 4010; ), responses( (status = 200, description = "Recording matching the ID in the path has been deleted"), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 404, description = "The specified recording was not found"), - (status = 409, description = "The recording is still ongoing and can't be deleted yet"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 404, description = "The specified recording was not found", body = ApiError, content_type = "application/problem+json"), + (status = 409, description = "The recording is still ongoing and can't be deleted yet", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.recording.delete"])), ))] @@ -278,11 +278,11 @@ pub(crate) struct DeleteManyResult { request_body(content = Vec, description = "JSON-encoded list of session IDs", content_type = "application/json"), responses( (status = 200, description = "Mass recording deletion task was successfully started", body = DeleteManyResult), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 409, description = "A recording is still ongoing and can't be deleted yet (nothing is deleted)"), - (status = 413, description = "Request payload is too large"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 409, description = "A recording is still ongoing and can't be deleted yet (nothing is deleted)", body = ApiError, content_type = "application/problem+json"), + (status = 413, description = "Request payload is too large", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.recording.delete"])), ))] @@ -437,9 +437,9 @@ async fn delete_recording(recording_path: &Utf8Path) -> anyhow::Result<()> { ), responses( (status = 200, description = "List of recordings on this Gateway instance", body = [Uuid]), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.recordings.read"])), ))] @@ -503,6 +503,7 @@ pub(crate) async fn list_recordings( ), responses( (status = 200, description = "ZIP archive containing the recording session", body = Vec, content_type = "application/zip"), + // No error body declared: generated clients pick the first JSON media type of all responses, and would expect JSON on success. (status = 401, description = "Invalid or missing authorization token"), (status = 403, description = "Insufficient permissions"), (status = 404, description = "Recording not found"), @@ -576,10 +577,10 @@ pub(crate) async fn pull_recording_session( "application/octet-stream", ], ), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 404, description = "File not found"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 404, description = "File not found", body = ApiError, content_type = "application/problem+json"), ), security(("jrec_token" = ["pull"])), ))] diff --git a/devolutions-gateway/src/api/jrl.rs b/devolutions-gateway/src/api/jrl.rs index d95f29af68..1888fdb1a5 100644 --- a/devolutions-gateway/src/api/jrl.rs +++ b/devolutions-gateway/src/api/jrl.rs @@ -24,10 +24,10 @@ pub fn make_router(state: DgwState) -> Router { path = "/jet/jrl", responses( (status = 200, description = "JRL updated successfully"), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to update the JRL"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to update the JRL", body = ApiError, content_type = "application/problem+json"), ), security(("jrl_token" = [])), ))] @@ -91,10 +91,10 @@ pub(crate) struct JrlInfo { path = "/jet/jrl/info", responses( (status = 200, description = "Current JRL Info", body = JrlInfo), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to update the JRL"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to update the JRL", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.jrl.read"])), ))] diff --git a/devolutions-gateway/src/api/monitoring.rs b/devolutions-gateway/src/api/monitoring.rs index 5e252574f2..2cc1436f2a 100644 --- a/devolutions-gateway/src/api/monitoring.rs +++ b/devolutions-gateway/src/api/monitoring.rs @@ -34,10 +34,10 @@ pub fn make_router(state: DgwState) -> Router { request_body(content = MonitorsConfig, description = "JSON object containing a list of monitors", content_type = "application/json"), responses( (status = 200, description = "New configuration was accepted, but not necessarily fully understood.", body = SetConfigResponse), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error while starting monitors"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error while starting monitors", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.net.monitor.config"])), ))] @@ -67,10 +67,10 @@ async fn handle_set_monitoring_config( path = "/jet/net/monitor/log/drain", responses( (status = 200, description = "Log was flushed and returned in the response body", body = MonitoringLogResponse), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.net.monitor.drain"])), ))] diff --git a/devolutions-gateway/src/api/net.rs b/devolutions-gateway/src/api/net.rs index c5d2d45acf..f51628f498 100644 --- a/devolutions-gateway/src/api/net.rs +++ b/devolutions-gateway/src/api/net.rs @@ -48,10 +48,11 @@ pub fn make_router(state: DgwState) -> Router { params(NetworkScanQueryParams), responses( (status = 101, description = "WebSocket upgrade; subsequent text frames carry NetworkScanResultEvent or LegacyScanEvent JSON"), + // Query validation errors carry their own JSON body, not ApiError. (status = 400, description = "Invalid query, mixed target/range, oversized range, or selected interface error"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("netscan_token" = [])), ))] @@ -646,10 +647,10 @@ impl TryFrom for (NetworkScannerParams, ScanEventFilter) path = "/jet/net/config", responses( (status = 200, description = "Network interfaces", body = [HashMap>]), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("netscan_token" = [])), ))] @@ -710,10 +711,10 @@ pub(crate) async fn get_net_config(_token: crate::extract::NetScanToken) -> Resu path = "/jet/net/interfaces", responses( (status = 200, description = "Gateway network scan sources", body = NetworkInterfacesResponse), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("netscan_token" = [])), ))] diff --git a/devolutions-gateway/src/api/preflight.rs b/devolutions-gateway/src/api/preflight.rs index 1477f737d2..769a760879 100644 --- a/devolutions-gateway/src/api/preflight.rs +++ b/devolutions-gateway/src/api/preflight.rs @@ -193,9 +193,9 @@ impl Outputs { request_body = [PreflightOperation], responses( (status = 200, description = "Preflight outputs", body = [PreflightOutput]), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.preflight"])), ))] diff --git a/devolutions-gateway/src/api/session.rs b/devolutions-gateway/src/api/session.rs index 19d7d45c4e..0b75a49f06 100644 --- a/devolutions-gateway/src/api/session.rs +++ b/devolutions-gateway/src/api/session.rs @@ -25,11 +25,11 @@ pub fn make_router(state: DgwState) -> Router { ), responses( (status = 200, description = "Session terminated successfully"), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 404, description = "No running session found with provided ID"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 404, description = "No running session found with provided ID", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.session.terminate"])), ))] diff --git a/devolutions-gateway/src/api/sessions.rs b/devolutions-gateway/src/api/sessions.rs index 37116ec67e..5b9b1f195d 100644 --- a/devolutions-gateway/src/api/sessions.rs +++ b/devolutions-gateway/src/api/sessions.rs @@ -19,10 +19,10 @@ pub fn make_router(state: DgwState) -> Router { path = "/jet/sessions", responses( (status = 200, description = "Running sessions", body = [SessionInfo]), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.sessions.read"])), ))] diff --git a/devolutions-gateway/src/api/traffic.rs b/devolutions-gateway/src/api/traffic.rs index 0a71436a8b..324b7e8a5d 100644 --- a/devolutions-gateway/src/api/traffic.rs +++ b/devolutions-gateway/src/api/traffic.rs @@ -31,10 +31,10 @@ pub fn make_router(state: crate::DgwState) -> Router { ), responses( (status = 200, description = "Successfully claimed traffic events", body = Vec), - (status = 400, description = "Invalid query parameters"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Internal server error"), + (status = 400, description = "Invalid query parameters", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Internal server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.traffic.claim"])), ))] @@ -89,11 +89,11 @@ pub(crate) async fn post_traffic_claim( request_body(content = AckRequest, description = "Array of event IDs to acknowledge", content_type = "application/json"), responses( (status = 200, description = "Successfully acknowledged events", body = AckResponse), - (status = 400, description = "Invalid request body (empty ids array)"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 413, description = "Payload too large (more than 10,000 IDs)"), - (status = 500, description = "Internal server error"), + (status = 400, description = "Invalid request body (empty ids array)", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 413, description = "Payload too large (more than 10,000 IDs)", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Internal server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.traffic.ack"])), ))] diff --git a/devolutions-gateway/src/api/tunnel.rs b/devolutions-gateway/src/api/tunnel.rs index f5ff604a10..10a29f7778 100644 --- a/devolutions-gateway/src/api/tunnel.rs +++ b/devolutions-gateway/src/api/tunnel.rs @@ -100,10 +100,10 @@ pub fn make_router(state: DgwState) -> Router { request_body(content = EnrollRequest, description = "Agent identity and certificate signing request", content_type = "application/json"), responses( (status = 200, description = "Agent enrolled", body = EnrollResponse), - (status = 400, description = "Invalid agent name, request body, or certificate signing request"), - (status = 401, description = "Invalid or missing enrollment token"), - (status = 409, description = "Agent ID or name already registered, agent key previously deleted, or enrollment token already used"), - (status = 500, description = "Unexpected server error"), + (status = 400, description = "Invalid agent name, request body, or certificate signing request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing enrollment token", body = ApiError, content_type = "application/problem+json"), + (status = 409, description = "Agent ID or name already registered, agent key previously deleted, or enrollment token already used", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("enrollment_token" = [])), ))] @@ -282,9 +282,9 @@ fn agent_info( path = "/jet/tunnel/agents", responses( (status = 200, description = "Accepted agents and their current status", body = [AgentInfo]), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Unexpected server error"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.agent.read"])), ))] @@ -322,10 +322,10 @@ pub(crate) async fn list_agents( ), responses( (status = 200, description = "Agent status", body = AgentInfo), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 404, description = "Agent not found"), - (status = 500, description = "Unexpected server error"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 404, description = "Agent not found", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.agent.read"])), ))] @@ -361,10 +361,10 @@ pub(crate) async fn get_agent( ), responses( (status = 204, description = "Agent deleted"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 404, description = "Agent not found"), - (status = 500, description = "Unexpected server error"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 404, description = "Agent not found", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Unexpected server error", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.agent.delete"])), ))] diff --git a/devolutions-gateway/src/api/update.rs b/devolutions-gateway/src/api/update.rs index ae1f832acb..a2c9bce46f 100644 --- a/devolutions-gateway/src/api/update.rs +++ b/devolutions-gateway/src/api/update.rs @@ -298,11 +298,11 @@ fn apply_products(req: UpdateRequest, manifest: &mut UpdateManifestV2, was_v2: b request_body(content = Option, description = "Products and target versions to update", content_type = "application/json"), responses( (status = 200, description = "Update request accepted", body = UpdateResponse), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to write update manifest"), - (status = 503, description = "Agent updater service is unavailable"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to write update manifest", body = ApiError, content_type = "application/problem+json"), + (status = 503, description = "Agent updater service is unavailable", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.update"])), ))] @@ -386,10 +386,10 @@ pub(crate) struct GetUpdateProductsResponse { path = "/jet/update", responses( (status = 200, description = "Installed product versions", body = GetUpdateProductsResponse), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to read agent status file"), - (status = 503, description = "Agent updater service is unavailable"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to read agent status file", body = ApiError, content_type = "application/problem+json"), + (status = 503, description = "Agent updater service is unavailable", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.update.read"])), ))] @@ -505,10 +505,10 @@ pub(crate) struct SetUpdateScheduleResponse {} path = "/jet/update/schedule", responses( (status = 200, description = "Current auto-update schedule", body = GetUpdateScheduleResponse), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), - (status = 500, description = "Failed to read agent status file"), - (status = 503, description = "Agent updater service is unavailable"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), + (status = 500, description = "Failed to read agent status file", body = ApiError, content_type = "application/problem+json"), + (status = 503, description = "Agent updater service is unavailable", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.update.read"])), ))] @@ -558,11 +558,11 @@ pub(super) async fn get_update_schedule(_scope: UpdateReadScope) -> Result $code:literal, + )+ + ) => { + /// Machine-readable reason for a rejected request. + /// + /// Only the code leaves the Gateway. + /// The details explaining it (error sources, key names, claim values…) are logged, never sent. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum ErrorCode { + $( + $( #[doc = $doc] )+ + $variant, + )+ + } + + impl ErrorCode { + /// All the codes, in declaration order. + pub const ALL: &[Self] = &[$( Self::$variant ),+]; + + /// Returns the snake_case string sent to clients. + pub const fn as_str(self) -> &'static str { + match self { + $( Self::$variant => $code, )+ + } + } + + /// Returns a one-sentence description of the code, for documentation purposes. + pub const fn description(self) -> &'static str { + match self { + $( Self::$variant => concat!($( $doc ),+).trim_ascii(), )+ + } + } + } + }; +} + +// Append new codes at the end: generated clients number enum members by position. +// Keep each description on a single line. +define_error_codes! { + // -- Generic codes, used when no specific code applies -- // + + /// The request is invalid. + BadRequest => "bad_request", + /// The request is not authenticated. + Unauthorized => "unauthorized", + /// The request is not allowed. + Forbidden => "forbidden", + /// The resource doesn't exist. + NotFound => "not_found", + /// The request took too long to complete. + RequestTimeout => "request_timeout", + /// The request conflicts with the current state of the resource. + Conflict => "conflict", + /// The request is too large. + PayloadTooLarge => "payload_too_large", + /// The Gateway failed to process the request. + InternalError => "internal_error", + /// The Gateway received an invalid response from an upstream server. + BadGateway => "bad_gateway", + /// The service is temporarily unavailable. + ServiceUnavailable => "service_unavailable", + /// An upstream server didn't respond in time. + GatewayTimeout => "gateway_timeout", + /// The Gateway is out of storage space. + InsufficientStorage => "insufficient_storage", + + // -- Token and authorization codes -- // + + /// No token was found in the `Authorization` header or in the `token` query parameter. + MissingToken => "missing_token", + /// The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. + MalformedToken => "malformed_token", + /// The token signature doesn't match the provisioner key of the Gateway. + BadSignature => "bad_signature", + /// The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. + UnknownSubkey => "unknown_subkey", + /// The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. + Expired => "expired", + /// The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. + NotYetValid => "not_yet_valid", + /// The token is restricted to another Gateway ID (`jet_gw_id` claim). + GatewayIdMismatch => "gateway_id_mismatch", + /// The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. + Revoked => "revoked", + /// The token was already used, and can't be used again in this context. + UnexpectedReplay => "unexpected_replay", + /// The token is rejected for a reason not covered by another code. + InvalidToken => "invalid_token", + /// The type of the token is not accepted by this route. + WrongTokenType => "wrong_token_type", + /// The scope of the token doesn't grant access to this route. + InsufficientScope => "insufficient_scope", +} + +impl fmt::Display for ErrorCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl serde::Serialize for ErrorCode { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(self.as_str()) + } +} + +#[cfg(feature = "openapi")] +impl<'s> utoipa::ToSchema<'s> for ErrorCode { + fn schema() -> (&'s str, utoipa::openapi::RefOr) { + use core::fmt::Write as _; + + use utoipa::openapi::schema::{ObjectBuilder, SchemaType}; + + let mut description = String::from( + "Machine-readable reason for a rejected request.\n\n\ + New codes can be added, but existing codes are never renamed or removed. \ + Clients must treat unknown codes as generic failures.\n", + ); + + for code in Self::ALL { + let _ = write!(description, "\n- `{}`: {}", code.as_str(), code.description()); + } + + let schema = ObjectBuilder::new() + .schema_type(SchemaType::String) + .description(Some(description)) + .enum_values(Some(Self::ALL.iter().map(|code| code.as_str()))) + .build(); + + ("ErrorCode", schema.into()) + } +} diff --git a/devolutions-gateway/src/extract.rs b/devolutions-gateway/src/extract.rs index b22ee04611..63bad6042e 100644 --- a/devolutions-gateway/src/extract.rs +++ b/devolutions-gateway/src/extract.rs @@ -5,6 +5,7 @@ use axum::extract::{ConnectInfo, FromRequest, FromRequestParts, Path, RawQuery, use axum::http::request::Parts; use crate::DgwState; +use crate::error_code::ErrorCode; use crate::http::HttpError; use crate::token::{ AccessScope, AccessTokenClaims, AssociationTokenClaims, BridgeTokenClaims, EnrollmentTokenClaims, JmuxTokenClaims, @@ -42,7 +43,9 @@ where if let AccessTokenClaims::Association(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected ASSOCIATION)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected ASSOCIATION)")) } } } @@ -60,7 +63,9 @@ where if let AccessTokenClaims::Jrl(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected JRL)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected JRL)")) } } } @@ -78,7 +83,9 @@ where if let AccessTokenClaims::Jrec(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected JREC)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected JREC)")) } } } @@ -96,7 +103,9 @@ where if let AccessTokenClaims::Jmux(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected JMUX)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected JMUX)")) } } } @@ -132,11 +141,13 @@ impl FromRequestParts for KdcToken { &state.recordings.active_recordings, None, ) - .map_err(HttpError::unauthorized().err())?; + .map_err(|error| HttpError::unauthorized().with_error_code(error.code()).build(error))?; match claims { AccessTokenClaims::Kdc(claims) => Ok(Self(claims)), - _ => Err(HttpError::forbidden().msg("token not allowed (expected KDC token)")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected KDC token)")), } } } @@ -154,7 +165,9 @@ where if let AccessTokenClaims::Scope(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected SCOPE)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected SCOPE)")) } } } @@ -172,7 +185,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::SessionsRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -190,7 +205,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::SessionTerminate => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -208,7 +225,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::AssociationsRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -226,7 +245,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::DiagnosticsRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -244,7 +265,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::JrlRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -262,7 +285,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::ConfigWrite => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -280,7 +305,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::HeartbeatRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -298,7 +325,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::RecordingDelete => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -316,7 +345,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::RecordingsRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -334,7 +365,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::Update => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -354,7 +387,9 @@ where // The full write scope also grants read access. AccessScope::Update => Ok(Self), AccessScope::UpdateRead => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -372,7 +407,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::Preflight => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -390,7 +427,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::TrafficClaim => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -408,7 +447,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::TrafficAck => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -426,7 +467,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::NetMonitorConfig => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -444,7 +487,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::NetMonitorDrain => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } @@ -471,9 +516,12 @@ where AccessTokenClaims::Scope(scope) => match scope.scope { AccessScope::Wildcard | AccessScope::AgentRead => Ok(Self), _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) .msg("invalid scope for agent management read (require one of: gateway.agent.read, *)")), }, - _ => Err(HttpError::forbidden().msg("scope token required for agent management read")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("scope token required for agent management read")), } } } @@ -500,9 +548,12 @@ where AccessTokenClaims::Scope(scope) => match scope.scope { AccessScope::Wildcard | AccessScope::AgentDelete => Ok(Self), _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) .msg("invalid scope for agent management delete (require one of: gateway.agent.delete, *)")), }, - _ => Err(HttpError::forbidden().msg("scope token required for agent management delete")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("scope token required for agent management delete")), } } } @@ -520,7 +571,9 @@ where if let AccessTokenClaims::WebApp(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected WEBAPP)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected WEBAPP)")) } } } @@ -538,7 +591,9 @@ where if let AccessTokenClaims::NetScan(_) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self) } else { - Err(HttpError::forbidden().msg("token not allowed (expected NETSCAN)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected NETSCAN)")) } } } @@ -556,7 +611,9 @@ where if let AccessTokenClaims::Bridge(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected BRIDGE)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected BRIDGE)")) } } } @@ -575,7 +632,9 @@ where if let AccessTokenClaims::Enrollment(claims) = AccessToken::from_request_parts(parts, state).await?.0 { Ok(Self(claims)) } else { - Err(HttpError::forbidden().msg("token not allowed (expected ENROLLMENT token)")) + Err(HttpError::forbidden() + .with_error_code(ErrorCode::WrongTokenType) + .msg("token not allowed (expected ENROLLMENT token)")) } } } diff --git a/devolutions-gateway/src/http.rs b/devolutions-gateway/src/http.rs index 5a85b9b8c4..01d60dce16 100644 --- a/devolutions-gateway/src/http.rs +++ b/devolutions-gateway/src/http.rs @@ -2,13 +2,17 @@ use core::fmt; use core::panic::Location; use std::error::Error as StdError; -use axum::http::StatusCode; +use axum::Json; +use axum::http::{HeaderValue, StatusCode, header}; use axum::response::{IntoResponse, Response}; +use crate::error_code::ErrorCode; + pub struct HttpErrorBuilder { pub code: StatusCode, pub loc: &'static Location<'static>, pub msg: Option<&'static str>, + pub error_code: Option, } impl HttpErrorBuilder { @@ -19,6 +23,7 @@ impl HttpErrorBuilder { code, loc: Location::caller(), msg: None, + error_code: None, } } @@ -28,6 +33,7 @@ impl HttpErrorBuilder { code: self.code, loc: self.loc, msg: self.msg, + error_code: self.error_code, source: Some(source.into()), } } @@ -38,6 +44,7 @@ impl HttpErrorBuilder { code: self.code, loc: self.loc, msg: self.msg, + error_code: self.error_code, source: Some(source.into()), } } @@ -49,12 +56,23 @@ impl HttpErrorBuilder { self } + /// Sets the machine-readable code sent to the client. + /// + /// Without one, the response carries the generic code matching the HTTP status. + #[inline] + #[must_use] + pub fn with_error_code(mut self, error_code: ErrorCode) -> HttpErrorBuilder { + self.error_code = Some(error_code); + self + } + #[inline] pub fn msg(self, msg: &'static str) -> HttpError { HttpError { code: self.code, loc: self.loc, msg: Some(msg), + error_code: self.error_code, source: None, } } @@ -63,7 +81,11 @@ impl HttpErrorBuilder { pub struct HttpError { pub code: StatusCode, pub loc: &'static Location<'static>, + /// Internal context, logged but never sent to the client. pub msg: Option<&'static str>, + /// Machine-readable code sent to the client. + pub error_code: Option, + /// Internal error, logged but never sent to the client. pub source: Option>, } @@ -127,8 +149,122 @@ impl fmt::Display for HttpError { impl IntoResponse for HttpError { fn into_response(self) -> Response { - error!(error = %self); - self.code.into_response() + let error_code = self.error_code.unwrap_or_else(|| generic_error_code(self.code)); + error!(error = %self, %error_code); + ApiError::new(self.code, error_code).into_response() + } +} + +/// Returns the code describing an HTTP status, for errors without a more specific code. +fn generic_error_code(status: StatusCode) -> ErrorCode { + match status { + StatusCode::BAD_REQUEST => ErrorCode::BadRequest, + StatusCode::UNAUTHORIZED => ErrorCode::Unauthorized, + StatusCode::FORBIDDEN => ErrorCode::Forbidden, + StatusCode::NOT_FOUND => ErrorCode::NotFound, + StatusCode::REQUEST_TIMEOUT => ErrorCode::RequestTimeout, + StatusCode::CONFLICT => ErrorCode::Conflict, + StatusCode::PAYLOAD_TOO_LARGE => ErrorCode::PayloadTooLarge, + StatusCode::INTERNAL_SERVER_ERROR => ErrorCode::InternalError, + StatusCode::BAD_GATEWAY => ErrorCode::BadGateway, + StatusCode::SERVICE_UNAVAILABLE => ErrorCode::ServiceUnavailable, + StatusCode::GATEWAY_TIMEOUT => ErrorCode::GatewayTimeout, + StatusCode::INSUFFICIENT_STORAGE => ErrorCode::InsufficientStorage, + _ if status.is_client_error() => ErrorCode::BadRequest, + _ => ErrorCode::InternalError, + } +} + +// Only the code is sent to the client: never add internal details, such as error sources or claim values. +// The `utoipa::path` annotations of the API handlers refer to this schema by name, without importing it. + +/// Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). +/// +/// The Gateway logs the details of the error, along with its code. +#[derive(Debug, Clone, Serialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +#[cfg_attr(feature = "openapi", schema( + example = json!({ "type": "about:blank", "title": "Unauthorized", "status": 401, "error": "gateway_id_mismatch" }) +))] +pub struct ApiError { + /// Problem type, always `about:blank`: the HTTP status and the error code describe the problem. + #[serde(rename = "type")] + problem_type: &'static str, + /// Reason phrase of the HTTP status. + title: &'static str, + /// HTTP status code. + #[serde(serialize_with = "serialize_status")] + #[cfg_attr(feature = "openapi", schema(value_type = u16))] + status: StatusCode, + /// Machine-readable error code; the `ErrorCode` schema lists the known values. + /// + /// New codes can be added: treat unknown values as generic failures. + // Typed as a string in the OpenAPI document, so that generated clients keep parsing the body when they get an unknown code. + #[cfg_attr(feature = "openapi", schema(value_type = String))] + error: ErrorCode, +} + +impl ApiError { + pub const CONTENT_TYPE: &'static str = "application/problem+json"; + + pub fn new(status: StatusCode, error: ErrorCode) -> Self { + Self { + problem_type: "about:blank", + title: status.canonical_reason().unwrap_or("Unknown"), + status, + error, + } + } +} + +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + let status = self.status; + let challenge = bearer_challenge(self.error); + + let mut response = (status, Json(self)).into_response(); + + let headers = response.headers_mut(); + headers.insert(header::CONTENT_TYPE, HeaderValue::from_static(Self::CONTENT_TYPE)); + if let Some(challenge) = challenge { + headers.insert(header::WWW_AUTHENTICATE, HeaderValue::from_static(challenge)); + } + + response + } +} + +fn serialize_status(status: &StatusCode, serializer: S) -> Result { + serializer.serialize_u16(status.as_u16()) +} + +/// Returns the `WWW-Authenticate` challenge for a rejected bearer token, as described by RFC 6750. +fn bearer_challenge(error_code: ErrorCode) -> Option<&'static str> { + match error_code { + ErrorCode::MissingToken => Some("Bearer"), + ErrorCode::MalformedToken + | ErrorCode::BadSignature + | ErrorCode::UnknownSubkey + | ErrorCode::Expired + | ErrorCode::NotYetValid + | ErrorCode::GatewayIdMismatch + | ErrorCode::Revoked + | ErrorCode::UnexpectedReplay + | ErrorCode::InvalidToken => Some(r#"Bearer error="invalid_token""#), + ErrorCode::InsufficientScope => Some(r#"Bearer error="insufficient_scope""#), + ErrorCode::BadRequest + | ErrorCode::Unauthorized + | ErrorCode::Forbidden + | ErrorCode::NotFound + | ErrorCode::RequestTimeout + | ErrorCode::Conflict + | ErrorCode::PayloadTooLarge + | ErrorCode::InternalError + | ErrorCode::BadGateway + | ErrorCode::ServiceUnavailable + | ErrorCode::GatewayTimeout + | ErrorCode::InsufficientStorage + | ErrorCode::WrongTokenType => None, } } diff --git a/devolutions-gateway/src/lib.rs b/devolutions-gateway/src/lib.rs index cd53900b26..b955112119 100644 --- a/devolutions-gateway/src/lib.rs +++ b/devolutions-gateway/src/lib.rs @@ -18,6 +18,7 @@ pub mod cli; pub mod config; pub mod credential; pub mod credential_injection; +pub mod error_code; pub mod extract; pub mod generic_client; pub mod http; @@ -158,7 +159,10 @@ pub fn make_http_service(state: DgwState) -> axum::Router<()> { )) // This middleware goes above `TimeoutLayer` because it will receive errors returned by `TimeoutLayer`. .layer(HandleErrorLayer::new(|_: axum::BoxError| async { - hyper::StatusCode::REQUEST_TIMEOUT + http::ApiError::new( + hyper::StatusCode::REQUEST_TIMEOUT, + error_code::ErrorCode::RequestTimeout, + ) })) .layer(TimeoutLayer::new(Duration::from_secs(15))), ) diff --git a/devolutions-gateway/src/middleware/auth.rs b/devolutions-gateway/src/middleware/auth.rs index c96462254b..7b1578288f 100644 --- a/devolutions-gateway/src/middleware/auth.rs +++ b/devolutions-gateway/src/middleware/auth.rs @@ -11,6 +11,7 @@ use axum_extra::headers::Authorization; use axum_extra::headers::authorization::Bearer; use crate::config::Conf; +use crate::error_code::ErrorCode; use crate::http::HttpError; use crate::recording::ActiveRecordings; use crate::session::DisconnectedInfo; @@ -135,6 +136,7 @@ pub async fn auth_middleware( let Ok(query) = serde_urlencoded::from_str::>(query) else { return Err(HttpError::unauthorized() + .with_error_code(ErrorCode::MissingToken) .msg("both authorization header and token query param invalid or missing")); }; @@ -163,19 +165,22 @@ pub async fn auth_middleware( let access_token_claims = match result { Ok(access_token_claims) => access_token_claims, Err(error) => { + // The event log entry carries the same code as the response. + let error_code = error.code(); + match &error { crate::token::TokenError::SignatureVerification { source, key } => { let _ = SYSTEM_LOGGER.emit( - sysevent_codes::jwt_rejected("bad_signature", format!("{source:#}")).field("key", key), + sysevent_codes::jwt_rejected(error_code.as_str(), format!("{source:#}")).field("key", key), ); } crate::token::TokenError::UnexpectedReplay { reason } => { - let _ = SYSTEM_LOGGER.emit(sysevent_codes::jwt_rejected("unexpected_replay", reason)); + let _ = SYSTEM_LOGGER.emit(sysevent_codes::jwt_rejected(error_code.as_str(), reason)); } _ => {} } - return Err(HttpError::unauthorized().err()(error)); + return Err(HttpError::unauthorized().with_error_code(error_code).build(error)); } }; diff --git a/devolutions-gateway/src/openapi.rs b/devolutions-gateway/src/openapi.rs index 5052f67046..03ab78818e 100644 --- a/devolutions-gateway/src/openapi.rs +++ b/devolutions-gateway/src/openapi.rs @@ -108,6 +108,8 @@ use crate::config::dto::{DataEncoding, PubKeyFormat, Subscriber}; crate::api::tunnel::AgentDomainAdvertisement, crate::api::tunnel::AgentStatus, crate::api::tunnel::AgentInfo, + crate::http::ApiError, + crate::error_code::ErrorCode, )), modifiers(&SecurityAddon), )] diff --git a/devolutions-gateway/src/token.rs b/devolutions-gateway/src/token.rs index d7aefef907..72803d0d5c 100644 --- a/devolutions-gateway/src/token.rs +++ b/devolutions-gateway/src/token.rs @@ -16,6 +16,7 @@ use smol_str::SmolStr; use thiserror::Error; use uuid::Uuid; +use crate::error_code::ErrorCode; use crate::recording::ActiveRecordings; use crate::session::DisconnectedInfo; use crate::target_addr::TargetAddr; @@ -893,6 +894,47 @@ pub enum TokenError { TooLongLifetime, } +impl TokenError { + /// Returns the machine-readable code reported to the client. + /// + /// The signature is verified before any claim is checked. + /// Errors raised before the signature check map to `malformed_token`, `bad_signature` or `unknown_subkey`, + /// so a caller without a token signed by a trusted key learns nothing about the claims. + pub fn code(&self) -> ErrorCode { + use picky::jose::jwt::JwtError; + + match self { + // Raised before the signature is verified. + Self::MissingDelegationKey | Self::Jwe { .. } | Self::JwePayload { .. } | Self::Jws { .. } => { + ErrorCode::MalformedToken + } + Self::SignatureVerification { .. } => ErrorCode::BadSignature, + Self::UnknownSubkey { .. } => ErrorCode::UnknownSubkey, + + // Raised after the signature is verified. + Self::Jwt { source } => match source { + JwtError::Expired { .. } => ErrorCode::Expired, + JwtError::NotYetValid { .. } => ErrorCode::NotYetValid, + JwtError::Json { .. } + | JwtError::InvalidRegisteredClaimType { .. } + | JwtError::RequiredClaimMissing { .. } => ErrorCode::MalformedToken, + _ => ErrorCode::InvalidToken, + }, + Self::BadContentType { .. } | Self::MalformedClaim { .. } | Self::InvalidClaimScheme { .. } => { + ErrorCode::MalformedToken + } + Self::GatewayIdScopeMismatch => ErrorCode::GatewayIdMismatch, + Self::Revoked => ErrorCode::Revoked, + Self::UnexpectedReplay { .. } => ErrorCode::UnexpectedReplay, + Self::ContentTypeNotAllowedForSubkey { .. } + | Self::PlaintextSecrets + | Self::OldJrl + | Self::SubkeySignedAndTooLongLifetime + | Self::TooLongLifetime => ErrorCode::InvalidToken, + } + } +} + #[derive(typed_builder::TypedBuilder)] pub struct TokenValidator<'a> { source_ip: IpAddr, diff --git a/docs/API-spec.md b/docs/API-spec.md index 27943ca4df..b907f57913 100644 --- a/docs/API-spec.md +++ b/docs/API-spec.md @@ -470,6 +470,67 @@ i.e.: } ``` +## Errors + +Error responses carry a machine-readable body, following [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (problem details for HTTP APIs), with the `application/problem+json` content type: + +```json +{ + "type": "about:blank", + "title": "Unauthorized", + "status": 401, + "error": "gateway_id_mismatch" +} +``` + +- `type` is always `about:blank`: the HTTP status and the error code describe the problem. +- `title` is the reason phrase of the HTTP status. +- `status` is the HTTP status code. +- `error` is the error code. + +.NET clients can read the body with the `ProblemDetails` type, which stores the error code in its `Extensions` dictionary, under the `error` key. + +The body never contains internal details, such as error messages, key names or claim values. +The Gateway logs these details along with the error code. + +Error codes are public API. +New codes can be added, but existing codes are never renamed or removed. +Clients must treat unknown codes as generic failures, and rely on the HTTP status. + +When no specific code applies, the error code is derived from the HTTP status: +`bad_request`, `unauthorized`, `forbidden`, `not_found`, `request_timeout`, `conflict`, `payload_too_large`, `internal_error`, `bad_gateway`, `service_unavailable`, `gateway_timeout` or `insufficient_storage`. + +Some errors don't carry this body yet, such as the errors raised while parsing a request body, and the `400` responses of `/jet/net/scan`, which have their own format. + +### Token and authorization errors + +| Code | Status | Meaning | +|------|--------|---------| +| `missing_token` | 401 | No token was found in the `Authorization` header or in the `token` query parameter. | +| `malformed_token` | 401 | The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. | +| `bad_signature` | 401 | The token signature doesn't match the provisioner key of the Gateway. | +| `unknown_subkey` | 401 | The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. | +| `expired` | 401 | The token has expired (`exp` claim). | +| `not_yet_valid` | 401 | The token is not valid yet (`nbf` claim). | +| `gateway_id_mismatch` | 401 | The token is restricted to another Gateway ID (`jet_gw_id` claim). | +| `revoked` | 401 | The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. | +| `unexpected_replay` | 401 | The token was already used, and can't be used again in this context. | +| `invalid_token` | 401 | The token is rejected for a reason not covered by another code. | +| `wrong_token_type` | 403 | The type of the token is not accepted by this route. | +| `insufficient_scope` | 403 | The scope of the token doesn't grant access to this route. | + +`expired` and `not_yet_valid` often mean that the clocks of the token issuer and of the Gateway are out of sync: compare them with `GET /jet/diagnostics/clock`. +For `gateway_id_mismatch`, the Gateway reports its ID in `GET /jet/health`, when requested with `Accept: application/json`. + +The Gateway verifies the token signature before checking any claim. +Unless the token is signed by a key the Gateway trusts, the error code is `missing_token`, `malformed_token`, `bad_signature` or `unknown_subkey`. + +Token errors also carry a `WWW-Authenticate` header, as described by [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750): + +- `Bearer` when the token is missing. +- `Bearer error="invalid_token"` when the token is rejected (status 401). +- `Bearer error="insufficient_scope"` when the scope of the token doesn't grant access to the route. + ## OpenAPI Endpoints are documented using [OpenAPI specification](../devolutions-gateway/openapi/doc/index.adoc). diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml index aa8fa4fc76..7f8b29fdde 100644 --- a/testsuite/Cargo.toml +++ b/testsuite/Cargo.toml @@ -18,23 +18,25 @@ harness = true [dependencies] anyhow = "1.0" assert_cmd = "2.2" +base64 = "0.23" dynosaur = "0.3" escargot = "0.5" expect-test = "1.5" fastrand = "2" +picky = { version = "7.0.0-rc.25", default-features = false, features = ["jose"] } serde_json = "1" serde = { version = "1", features = ["derive"] } tempfile = "3" tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "net", "process"] } tokio-util = "0.7" typed-builder = "0.21" +uuid = { version = "1", features = ["v4"] } tokio-tungstenite = { version = "0.29", features = ["rustls-tls-native-roots"] } [dev-dependencies] agent-sysevent-codes.path = "../crates/agent-sysevent-codes" agent-tunnel = { path = "../crates/agent-tunnel", features = ["test-utils"] } agent-tunnel-proto = { path = "../crates/agent-tunnel-proto", features = ["serde"] } -base64 = "0.23" camino = "1" devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } devolutions-gateway = { path = "../devolutions-gateway" } @@ -48,7 +50,6 @@ mcp-proxy.path = "../crates/mcp-proxy" network-scanner = { path = "../crates/network-scanner", features = ["test-utils"] } network-scanner-proto = { path = "../crates/network-scanner-proto" } nonempty = "0.12" -picky = { version = "7.0.0-rc.25", default-features = false, features = ["jose"] } proxy-socks = { path = "../crates/proxy-socks" } quinn = "0.11" rcgen = { version = "0.13", features = ["pem", "x509-parser"] } @@ -63,7 +64,6 @@ sysevent-codes.path = "../crates/sysevent-codes" tempfile = "3" test-utils.path = "../crates/test-utils" tokio-rustls = { version = "0.26", features = ["ring"] } -uuid = { version = "1", features = ["v4"] } [target.'cfg(unix)'.dev-dependencies] sysevent-syslog.path = "../crates/sysevent-syslog" diff --git a/testsuite/src/dgw_config.rs b/testsuite/src/dgw_config.rs index f55f6f7c1f..6318ff4837 100644 --- a/testsuite/src/dgw_config.rs +++ b/testsuite/src/dgw_config.rs @@ -59,6 +59,9 @@ impl fmt::Display for ProxyMode { #[derive(TypedBuilder)] pub struct DgwConfig { + /// Gateway ID, matched against the `jet_gw_id` token claim. + #[builder(default, setter(into))] + id: Option, #[builder(default, setter(into))] hostname: Option, #[builder(default = "127.0.0.1".to_owned(), setter(into))] @@ -125,6 +128,7 @@ pub struct DgwConfigHandle { impl DgwConfigHandle { pub fn init(config: DgwConfig) -> anyhow::Result { let DgwConfig { + id, hostname, listener_host, provisioner_public_key_data, @@ -194,6 +198,15 @@ impl DgwConfigHandle { }) .unwrap_or_default(); + let id_json = id + .map(|id| { + format!( + r#" "Id": "{id}", +"# + ) + }) + .unwrap_or_default(); + let hostname_json = hostname .map(|hostname| { format!( @@ -208,7 +221,7 @@ impl DgwConfigHandle { let config = format!( r#"{{ -{hostname_json} "ProvisionerPublicKeyData": {{ +{id_json}{hostname_json} "ProvisionerPublicKeyData": {{ "Value": "{provisioner_public_key_data}" }}, "Listeners": [ diff --git a/testsuite/src/lib.rs b/testsuite/src/lib.rs index f69cbd5519..3e94839082 100644 --- a/testsuite/src/lib.rs +++ b/testsuite/src/lib.rs @@ -9,3 +9,4 @@ pub mod dgw_config; pub mod http; pub mod mcp_client; pub mod mcp_server; +pub mod token; diff --git a/testsuite/src/token.rs b/testsuite/src/token.rs new file mode 100644 index 0000000000..143dd9d4fd --- /dev/null +++ b/testsuite/src/token.rs @@ -0,0 +1,93 @@ +//! Token signing helpers, for tests running the Gateway with token validation enabled. + +use base64::Engine as _; +use picky::jose::jws::JwsAlg; +use picky::jose::jwt::CheckedJwtSig; +use picky::key::PrivateKey; + +/// Private key matching [`provisioner_public_key_data`]. +const PROVISIONER_KEY: &str = "-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkrPiL/5dmGIT5 +/KuC3H/jIjeLoLoddsLhAlikO5JQQo3Zs71GwT4Wd2z8WLMe0lVZu/Jr2S28p0M8 +F3Lnz4IgzjocQomFgucFWWQRyD03ZE2BHfEeelFsp+/4GZaM6lKZauYlIMtjR1vD +lflgvxNTr0iaii4JR9K3IKCunCRy1HQYPcZ9waNtlG5xXtW9Uf1tLWPJpP/3I5HL +M85JPBv4r286vpeUlfQIa/NB4g5w6KZ6MfEAIU4KeEQpeLAyyYvwUzPR2uQZ4y4I +4Nj84dWYB1cMTlSGugvSgOFKYit1nwLGeA7EevVYPbILRfSMBU/+avGNJJ8HCaaq +FIyY42W9AgMBAAECggEBAImsGXcvydaNrIFUvW1rkxML5qUJfwN+HJWa9ALsWoo3 +h28p5ypR7S9ZdyP1wuErgHcl0C1d80tA6BmlhGhLZeyaPCIHbQQUa0GtL7IE+9X9 +bSvu+tt+iMcB1FdqEFmGOXRkB2sS82Ax9e0qvZihcOFRBkUEK/MqapIV8qctGkSG +wIE6yn5LHRls/fJU8BJeeqJmYpuWljipwTkp9hQ7SdRYFLNjwjlz/b0hjmgFs5QZ +LUNMyTHdHtXQHNsf/GayRUAKf5wzN/jru+nK6lMob2Ehfx9/RAfgaDHzy5BNFMj0 +i9+sAycgIW1HpTuDvSEs3qP26NeQ82GbJzATmdAKa4ECgYEA9Vti0YG+eXJI3vdS +uXInU0i1SY4aEG397OlGMwh0yQnp2KGruLZGkTvqxG/Adj1ObDyjFH9XUhMrd0za +Nk/VJFybWafljUPcrfyPAVLQLjsBfMg3Y34sTF6QjUnhg49X2jfvy9QpC5altCtA +46/KVAGREnQJ3wMjfGGIFP8BUZsCgYEA7phYE/cYyWg7a/o8eKOFGqs11ojSqG3y +0OE7kvW2ugUuy3ex+kr19Q/8pOWEc7M1UEV8gmc11xgB70EhIFt9Jq379H0X4ahS ++mgLiPzKAdNCRPpkxwwN9HxFDgGWoYcgMplhoAmg9lWSDuE1Exy8iu5inMWuF4MT +/jG+cLnUZ4cCgYAfMIXIUjDvaUrAJTp73noHSUfaWNkRW5oa4rCMzjdiUwNKCYs1 +yN4BmldGr1oM7dApTDAC7AkiotM0sC1RGCblH2yUIha5NXY5G9Dl/yv9pHyU6zK3 +UBO7hY3kmA611aP6VoACLi8ljPn1hEYUa4VR1n0llmCm29RH/HH7EUuOnwKBgExH +OCFp5eq+AAFNRvfqjysvgU7M/0wJmo9c8obRN1HRRlyWL7gtLuTh74toNSgoKus2 +y8+E35mce0HaOJT3qtMq3FoVhAUIoz6a9NUevBZJS+5xfraEDBIViJ4ps9aANLL4 +hlV7vpICWWeYaDdsAHsKK0yjhjzOEx45GQFA578RAoGBAOB42BG53tL0G9pPeJPt +S2LM6vQKeYx+gXTk6F335UTiiC8t0CgNNQUkW105P/SdpCTTKojAsOPMKOF7z4mL +lj/bWmNq7xu9uVOcBKrboVFGO/n6FXyWZxHPOTdjTkpe8kvvmSwl2iaTNllvSr46 +Z/fDKMxHxeXla54kfV+HiGkH +-----END PRIVATE KEY-----"; + +/// Private key the Gateway doesn't know about. +const UNTRUSTED_KEY: &str = "-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw +kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr +m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi +NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV +3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2 +QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs +kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go +amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM ++bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9 +D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC +0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y +lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+ +hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp +bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X ++jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B +BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC +2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx +QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz +5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9 +Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0 +NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j +8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma +3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K +y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB +jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE= +-----END RSA PRIVATE KEY-----"; + +/// Returns the provisioner private key, whose public key [`provisioner_public_key_data`] returns. +pub fn provisioner_key() -> PrivateKey { + PrivateKey::from_pem_str(PROVISIONER_KEY).expect("parse the provisioner key") +} + +/// Returns a private key the Gateway doesn't trust. +pub fn untrusted_key() -> PrivateKey { + PrivateKey::from_pem_str(UNTRUSTED_KEY).expect("parse the untrusted key") +} + +/// Returns the provisioner public key, encoded for [`crate::dgw_config::DgwConfig`]. +pub fn provisioner_public_key_data() -> String { + let der = provisioner_key() + .to_public_key() + .and_then(|public_key| public_key.to_der()) + .expect("encode the provisioner public key"); + + // The `m` multibase prefix stands for base64 without padding. + format!("m{}", base64::engine::general_purpose::STANDARD_NO_PAD.encode(der)) +} + +/// Signs a token with the given content type (`cty` header) and key ID (`kid` header). +pub fn sign_token(key: &PrivateKey, content_type: &str, kid: Option<&str>, claims: &serde_json::Value) -> String { + let mut jwt = CheckedJwtSig::new_with_cty(JwsAlg::RS256, content_type, claims); + jwt.header.kid = kid.map(str::to_owned); + jwt.encode(key).expect("sign the token") +} diff --git a/testsuite/tests/cli/dgw/api_errors.rs b/testsuite/tests/cli/dgw/api_errors.rs new file mode 100644 index 0000000000..c33332319d --- /dev/null +++ b/testsuite/tests/cli/dgw/api_errors.rs @@ -0,0 +1,323 @@ +//! Machine-readable error responses of the HTTP API, with token validation enabled. + +use std::time::{Duration, SystemTime}; + +use rstest::rstest; +use serde_json::{Value, json}; +use testsuite::cli::start_dgw; +use testsuite::dgw_config::{DgwConfig, DgwConfigHandle}; +use testsuite::token::{provisioner_key, provisioner_public_key_data, sign_token, untrusted_key}; +use uuid::Uuid; + +const GATEWAY_ID: Uuid = Uuid::from_u128(0x0b7b_8c8e_6e61_4a2c_9a3f_5d1e_7c2b_4f10); + +const INVALID_TOKEN_CHALLENGE: &str = r#"Bearer error="invalid_token""#; + +/// Gateway running with token validation enabled, trusting the provisioner key of the testsuite. +struct Gateway { + config: DgwConfigHandle, + process: tokio::process::Child, +} + +impl Gateway { + async fn start() -> Self { + let config = DgwConfig::builder() + .id(GATEWAY_ID) + .provisioner_public_key_data(provisioner_public_key_data()) + .build() + .init() + .expect("init gateway config"); + let process = start_dgw(&config).await.expect("start gateway"); + + Self { config, process } + } + + async fn request(&self, method: reqwest::Method, path: &str, token: Option<&str>) -> Response { + let url = format!("http://127.0.0.1:{}{path}", self.config.http_port()); + let mut request = reqwest::Client::new() + .request(method, url) + .timeout(Duration::from_secs(10)); + + if let Some(token) = token { + request = request.bearer_auth(token); + } + + let response = request.send().await.expect("send request"); + + let header = |name| { + response + .headers() + .get(name) + .map(|value| value.to_str().expect("header value is ASCII").to_owned()) + }; + + Response { + status: response.status().as_u16(), + content_type: header(reqwest::header::CONTENT_TYPE), + www_authenticate: header(reqwest::header::WWW_AUTHENTICATE), + body: response.text().await.expect("read response body"), + } + } + + async fn get_heartbeat(&self, token: Option<&str>) -> Response { + self.request(reqwest::Method::GET, "/jet/heartbeat", token).await + } + + async fn stop(mut self) { + let _ = self.process.start_kill(); + let _ = self.process.wait().await; + } +} + +#[derive(Debug)] +struct Response { + status: u16, + content_type: Option, + www_authenticate: Option, + body: String, +} + +impl Response { + /// Asserts that the response is a problem details error, and that the body carries nothing but the code. + #[track_caller] + fn assert_problem(&self, status: u16, title: &str, error: &str, challenge: Option<&str>) { + assert_eq!(self.status, status, "{self:?}"); + assert_eq!( + self.content_type.as_deref(), + Some("application/problem+json"), + "{self:?}" + ); + assert_eq!(self.www_authenticate.as_deref(), challenge, "{self:?}"); + + let body: Value = serde_json::from_str(&self.body).expect("JSON body"); + assert_eq!( + body, + json!({ "type": "about:blank", "title": title, "status": status, "error": error }), + ); + } + + #[track_caller] + fn assert_unauthorized(&self, error: &str) { + self.assert_problem(401, "Unauthorized", error, Some(INVALID_TOKEN_CHALLENGE)); + } +} + +fn now() -> i64 { + let since_epoch = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("time after UNIX epoch"); + i64::try_from(since_epoch.as_secs()).expect("timestamp fits in i64") +} + +/// Claims of a scope token granting access to `GET /jet/heartbeat`. +fn heartbeat_claims() -> Value { + let now = now(); + json!({ + "jti": Uuid::new_v4(), + "nbf": now - 60, + "exp": now + 3600, + "scope": "gateway.heartbeat.read", + }) +} + +fn with_claims(mut claims: Value, overrides: Value) -> Value { + let claims_map = claims.as_object_mut().expect("claims object"); + for (name, value) in overrides.as_object().expect("overrides object") { + claims_map.insert(name.clone(), value.clone()); + } + claims +} + +fn without_claim(mut claims: Value, name: &str) -> Value { + claims.as_object_mut().expect("claims object").remove(name); + claims +} + +fn expired_claims() -> Value { + with_claims(heartbeat_claims(), json!({ "nbf": now() - 7200, "exp": now() - 3600 })) +} + +fn not_yet_valid_claims() -> Value { + with_claims(heartbeat_claims(), json!({ "nbf": now() + 3600, "exp": now() + 7200 })) +} + +fn other_gateway_claims() -> Value { + with_claims(heartbeat_claims(), json!({ "jet_gw_id": Uuid::new_v4() })) +} + +fn scope_token(claims: &Value) -> String { + sign_token(&provisioner_key(), "SCOPE", None, claims) +} + +/// A BRIDGE token whose lifetime exceeds the 12-hour maximum. +fn too_long_bridge_token() -> String { + let now = now(); + let claims = json!({ + "target_host": "http://localhost", + "jet_aid": Uuid::new_v4(), + "nbf": now - 60, + "exp": now + 13 * 3600, + }); + sign_token(&provisioner_key(), "BRIDGE", None, &claims) +} + +fn jrl_token(jrl: Value) -> String { + let claims = json!({ "jti": Uuid::new_v4(), "iat": now(), "jrl": jrl }); + sign_token(&provisioner_key(), "JRL", None, &claims) +} + +#[tokio::test] +async fn missing_token() { + let gateway = Gateway::start().await; + + let response = gateway.get_heartbeat(None).await; + + // Per RFC 6750, the challenge carries no error code when the request has no token at all. + response.assert_problem(401, "Unauthorized", "missing_token", Some("Bearer")); + + gateway.stop().await; +} + +#[rstest] +#[case::malformed_token("not-a-token".to_owned(), "malformed_token")] +#[case::malformed_claims(scope_token(&without_claim(heartbeat_claims(), "scope")), "malformed_token")] +#[case::bad_signature(sign_token(&untrusted_key(), "SCOPE", None, &heartbeat_claims()), "bad_signature")] +#[case::unknown_subkey(sign_token(&provisioner_key(), "SCOPE", Some("unknown"), &heartbeat_claims()), "unknown_subkey")] +#[case::expired(scope_token(&expired_claims()), "expired")] +#[case::not_yet_valid(scope_token(¬_yet_valid_claims()), "not_yet_valid")] +#[case::gateway_id_mismatch(scope_token(&other_gateway_claims()), "gateway_id_mismatch")] +#[case::invalid_token(too_long_bridge_token(), "invalid_token")] +#[tokio::test] +async fn rejected_token(#[case] token: String, #[case] expected_error: &str) { + let gateway = Gateway::start().await; + + let response = gateway.get_heartbeat(Some(&token)).await; + + response.assert_unauthorized(expected_error); + + gateway.stop().await; +} + +/// The signature is verified before any claim: without a token signed by a trusted key, +/// a caller learns nothing about the claims the Gateway expects. +#[rstest] +#[case::expired(expired_claims())] +#[case::not_yet_valid(not_yet_valid_claims())] +#[case::gateway_id_mismatch(other_gateway_claims())] +#[case::malformed_claims(without_claim(heartbeat_claims(), "scope"))] +#[tokio::test] +async fn claims_are_not_disclosed_without_a_valid_signature(#[case] claims: Value) { + let gateway = Gateway::start().await; + + let token = sign_token(&untrusted_key(), "SCOPE", None, &claims); + let response = gateway.get_heartbeat(Some(&token)).await; + + response.assert_unauthorized("bad_signature"); + + gateway.stop().await; +} + +#[tokio::test] +async fn unexpected_replay() { + let gateway = Gateway::start().await; + + let token = scope_token(&heartbeat_claims()); + + let first = gateway.get_heartbeat(Some(&token)).await; + assert_eq!(first.status, 200, "{first:?}"); + + // Scope tokens can't be reused. + let second = gateway.get_heartbeat(Some(&token)).await; + second.assert_unauthorized("unexpected_replay"); + + gateway.stop().await; +} + +#[tokio::test] +async fn revoked() { + let gateway = Gateway::start().await; + + let claims = heartbeat_claims(); + + let jrl_update = gateway + .request( + reqwest::Method::POST, + "/jet/jrl", + Some(&jrl_token(json!({ "jti": [claims["jti"]] }))), + ) + .await; + assert_eq!(jrl_update.status, 200, "{jrl_update:?}"); + + let response = gateway.get_heartbeat(Some(&scope_token(&claims))).await; + + response.assert_unauthorized("revoked"); + + gateway.stop().await; +} + +#[tokio::test] +async fn wrong_token_type() { + let gateway = Gateway::start().await; + + // A valid token, but the route expects a SCOPE token. + let response = gateway.get_heartbeat(Some(&jrl_token(json!({})))).await; + + response.assert_problem(403, "Forbidden", "wrong_token_type", None); + + gateway.stop().await; +} + +#[tokio::test] +async fn insufficient_scope() { + let gateway = Gateway::start().await; + + let claims = with_claims(heartbeat_claims(), json!({ "scope": "gateway.sessions.read" })); + let response = gateway.get_heartbeat(Some(&scope_token(&claims))).await; + + response.assert_problem( + 403, + "Forbidden", + "insufficient_scope", + Some(r#"Bearer error="insufficient_scope""#), + ); + + gateway.stop().await; +} + +#[tokio::test] +async fn generic_code_derived_from_status() { + let gateway = Gateway::start().await; + + let claims = with_claims(heartbeat_claims(), json!({ "scope": "gateway.session.terminate" })); + let path = format!("/jet/session/{}/terminate", Uuid::new_v4()); + let response = gateway + .request(reqwest::Method::POST, &path, Some(&scope_token(&claims))) + .await; + + response.assert_problem(404, "Not Found", "not_found", None); + + gateway.stop().await; +} + +/// The KDC proxy reads its token from the path, and validates it outside of the authentication middleware. +#[tokio::test] +async fn rejected_kdc_token() { + let gateway = Gateway::start().await; + + let now = now(); + let claims = json!({ + "krb_realm": "example.com", + "krb_kdc": "tcp://127.0.0.1:88", + "jti": Uuid::new_v4(), + "nbf": now - 60, + "exp": now + 3600, + }); + let token = sign_token(&untrusted_key(), "KDC", None, &claims); + let response = gateway + .request(reqwest::Method::POST, &format!("/jet/KdcProxy/{token}"), None) + .await; + + response.assert_unauthorized("bad_signature"); + + gateway.stop().await; +} diff --git a/testsuite/tests/cli/dgw/mod.rs b/testsuite/tests/cli/dgw/mod.rs index bbe2ad866f..4f966dfe9b 100644 --- a/testsuite/tests/cli/dgw/mod.rs +++ b/testsuite/tests/cli/dgw/mod.rs @@ -1,3 +1,4 @@ +mod api_errors; mod benign_disconnect; mod cli_args; mod heartbeat; diff --git a/testsuite/tests/error_codes.rs b/testsuite/tests/error_codes.rs new file mode 100644 index 0000000000..2b221c6b6f --- /dev/null +++ b/testsuite/tests/error_codes.rs @@ -0,0 +1,138 @@ +//! Machine-readable error codes reported to clients. + +use std::collections::HashSet; + +use devolutions_gateway::error_code::ErrorCode; +use devolutions_gateway::token::{ContentType, TokenError}; +use expect_test::expect; +use picky::jose::jwe::JweError; +use picky::jose::jws::JwsError; +use picky::jose::jwt::{JwtDate, JwtError}; +use rstest::rstest; + +fn invalid_utf8() -> std::str::Utf8Error { + std::str::from_utf8(std::hint::black_box(&[0xC0])).unwrap_err() +} + +fn json_error() -> serde_json::Error { + serde_json::from_str::("not JSON").unwrap_err() +} + +fn jws_error() -> JwsError { + JwsError::InvalidEncoding { input: String::new() } +} + +#[rstest] +// Raised before the signature is verified. +#[case::missing_delegation_key(TokenError::MissingDelegationKey, "malformed_token")] +#[case::jwe(TokenError::Jwe { source: JweError::AesGcm }, "malformed_token")] +#[case::jwe_payload(TokenError::JwePayload { source: invalid_utf8() }, "malformed_token")] +#[case::jws(TokenError::Jws { source: jws_error() }, "malformed_token")] +#[case::signature_verification( + TokenError::SignatureVerification { source: jws_error(), key: "main provisioner key" }, + "bad_signature" +)] +#[case::unknown_subkey(TokenError::UnknownSubkey { provided_kid: String::new() }, "unknown_subkey")] +// Raised after the signature is verified. +#[case::bad_content_type( + TokenError::BadContentType { source: "UNKNOWN".parse::().unwrap_err() }, + "malformed_token" +)] +#[case::expired( + TokenError::Jwt { source: JwtError::Expired { not_after: 0, now: JwtDate::new(1) } }, + "expired" +)] +#[case::not_yet_valid( + TokenError::Jwt { source: JwtError::NotYetValid { not_before: 1, now: JwtDate::new(0) } }, + "not_yet_valid" +)] +#[case::required_claim_missing( + TokenError::Jwt { source: JwtError::RequiredClaimMissing { claim: "exp" } }, + "malformed_token" +)] +#[case::invalid_registered_claim_type( + TokenError::Jwt { source: JwtError::InvalidRegisteredClaimType { claim: "exp" } }, + "malformed_token" +)] +#[case::claims_json(TokenError::Jwt { source: JwtError::Json { source: json_error() } }, "malformed_token")] +#[case::invalid_validator( + TokenError::Jwt { source: JwtError::InvalidValidator { description: "" } }, + "invalid_token" +)] +#[case::content_type_not_allowed_for_subkey( + TokenError::ContentTypeNotAllowedForSubkey { content_type: ContentType::Scope }, + "invalid_token" +)] +#[case::malformed_claim( + TokenError::MalformedClaim { name: "jet_gw_id", source: anyhow::anyhow!("invalid UUID") }, + "malformed_token" +)] +#[case::gateway_id_scope_mismatch(TokenError::GatewayIdScopeMismatch, "gateway_id_mismatch")] +#[case::revoked(TokenError::Revoked, "revoked")] +#[case::invalid_claim_scheme( + TokenError::InvalidClaimScheme { content_type: ContentType::Scope, source: json_error() }, + "malformed_token" +)] +#[case::plaintext_secrets(TokenError::PlaintextSecrets, "invalid_token")] +#[case::unexpected_replay(TokenError::UnexpectedReplay { reason: "different source IP" }, "unexpected_replay")] +#[case::old_jrl(TokenError::OldJrl, "invalid_token")] +#[case::subkey_signed_and_too_long_lifetime(TokenError::SubkeySignedAndTooLongLifetime, "invalid_token")] +#[case::too_long_lifetime(TokenError::TooLongLifetime, "invalid_token")] +fn token_error_code(#[case] error: TokenError, #[case] expected_code: &str) { + assert_eq!(error.code().as_str(), expected_code); +} + +/// Codes are public API: existing codes are never renamed or removed, and new codes are appended. +#[test] +fn codes_are_stable() { + let codes = ErrorCode::ALL + .iter() + .map(|code| code.as_str()) + .collect::>() + .join("\n"); + + expect![[r#" + bad_request + unauthorized + forbidden + not_found + request_timeout + conflict + payload_too_large + internal_error + bad_gateway + service_unavailable + gateway_timeout + insufficient_storage + missing_token + malformed_token + bad_signature + unknown_subkey + expired + not_yet_valid + gateway_id_mismatch + revoked + unexpected_replay + invalid_token + wrong_token_type + insufficient_scope"#]] + .assert_eq(&codes); +} + +#[test] +fn codes_are_unique_snake_case_and_documented() { + let mut seen = HashSet::new(); + + for code in ErrorCode::ALL { + let code_str = code.as_str(); + + assert!(seen.insert(code_str), "duplicated code: {code_str}"); + assert!( + code_str.starts_with(|c: char| c.is_ascii_lowercase()) + && code_str.chars().all(|c| c.is_ascii_lowercase() || c == '_'), + "not snake_case: {code_str}" + ); + assert!(!code.description().is_empty(), "undocumented code: {code_str}"); + assert_eq!(serde_json::to_value(code).unwrap(), code_str); + } +} diff --git a/testsuite/tests/main.rs b/testsuite/tests/main.rs index 4b5861aa83..7707aa3c93 100644 --- a/testsuite/tests/main.rs +++ b/testsuite/tests/main.rs @@ -4,6 +4,7 @@ mod agent_tunnel; mod cli; +mod error_codes; mod mcp_proxy; mod network_scanner; #[cfg(windows)] From a74b0fedfceb022149eb9a56fbe4f02aef1ee122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Thu, 15 Oct 2026 17:06:04 +0900 Subject: [PATCH 2/5] chore(openapi): regenerate the spec and clients for API error responses Issue: DGW-467 Co-Authored-By: Claude Opus 5.5 --- devolutions-gateway/openapi/doc/index.adoc | 374 +++++++++---- .../dotnet-client/.openapi-generator/FILES | 4 + .../openapi/dotnet-client/README.md | 4 +- .../openapi/dotnet-client/docs/AgentApi.md | 8 +- .../openapi/dotnet-client/docs/ApiError.md | 14 + .../openapi/dotnet-client/docs/ConfigApi.md | 2 +- .../dotnet-client/docs/DiagnosticsApi.md | 2 +- .../openapi/dotnet-client/docs/ErrorCode.md | 10 + .../dotnet-client/docs/HeartbeatApi.md | 2 +- .../openapi/dotnet-client/docs/JrecApi.md | 8 +- .../openapi/dotnet-client/docs/JrlApi.md | 4 +- .../openapi/dotnet-client/docs/NetApi.md | 6 +- .../docs/NetworkMonitoringApi.md | 4 +- .../dotnet-client/docs/PreflightApi.md | 2 +- .../openapi/dotnet-client/docs/SessionsApi.md | 4 +- .../openapi/dotnet-client/docs/TrafficApi.md | 4 +- .../openapi/dotnet-client/docs/UpdateApi.md | 8 +- .../Api/AgentApi.cs | 22 +- .../Api/ConfigApi.cs | 4 +- .../Api/DiagnosticsApi.cs | 8 +- .../Api/HealthApi.cs | 2 +- .../Api/HeartbeatApi.cs | 8 +- .../Devolutions.Gateway.Client/Api/JrecApi.cs | 30 +- .../Devolutions.Gateway.Client/Api/JrlApi.cs | 10 +- .../Devolutions.Gateway.Client/Api/NetApi.cs | 16 +- .../Api/NetworkMonitoringApi.cs | 14 +- .../Api/PreflightApi.cs | 8 +- .../Api/SessionsApi.cs | 10 +- .../Api/TrafficApi.cs | 14 +- .../Api/UpdateApi.cs | 26 +- .../Api/WebAppApi.cs | 2 +- .../Client/ApiClient.cs | 2 +- .../Client/ApiException.cs | 2 +- .../Client/ApiResponse.cs | 2 +- .../Client/ClientUtils.cs | 2 +- .../Client/Configuration.cs | 4 +- .../Client/ExceptionFactory.cs | 2 +- .../Client/FileParameter.cs | 2 +- .../Client/GlobalConfiguration.cs | 2 +- .../Client/IApiAccessor.cs | 2 +- .../Client/IAsynchronousClient.cs | 2 +- .../Client/IReadableConfiguration.cs | 2 +- .../Client/ISynchronousClient.cs | 2 +- .../Client/Multimap.cs | 2 +- .../Client/OpenAPIDateConverter.cs | 2 +- .../Client/RequestOptions.cs | 2 +- .../Client/RetryConfiguration.cs | 2 +- .../Client/WebRequestPathBuilder.cs | 2 +- .../Model/AbstractOpenAPISchema.cs | 2 +- .../Model/AccessScope.cs | 2 +- .../Model/AckRequest.cs | 2 +- .../Model/AckResponse.cs | 2 +- .../Model/AddressFamily.cs | 2 +- .../Model/AgentDomainAdvertisement.cs | 2 +- .../Model/AgentInfo.cs | 2 +- .../Model/AgentStatus.cs | 2 +- .../Model/ApiError.cs | 141 +++++ .../Model/AppCredential.cs | 2 +- .../Model/AppCredentialKind.cs | 2 +- .../Model/AppTokenContentType.cs | 2 +- .../Model/AppTokenSignRequest.cs | 2 +- .../Model/ClaimedTrafficEvent.cs | 2 +- .../Model/ClockDiagnostic.cs | 2 +- .../Model/ConfigDiagnostic.cs | 2 +- .../Model/ConfigPatch.cs | 2 +- .../Model/ConnectionMode.cs | 2 +- .../Model/DataEncoding.cs | 2 +- .../Model/DeleteManyResult.cs | 2 +- .../Model/EnrollRequest.cs | 2 +- .../Model/EnrollResponse.cs | 2 +- .../Model/ErrorCode.cs | 245 ++++++++ .../Model/EventOutcomeResponse.cs | 2 +- .../Model/GetUpdateProductsResponse.cs | 2 +- .../Model/GetUpdateScheduleResponse.cs | 2 +- .../Model/Heartbeat.cs | 2 +- .../Model/HostScanStateDto.cs | 2 +- .../Model/Identity.cs | 2 +- .../Model/InterfaceInfo.cs | 2 +- .../Model/JrlInfo.cs | 2 +- .../Model/ListenerUrls.cs | 2 +- .../Model/MonitorDefinition.cs | 2 +- .../Model/MonitorDefinitionProbeTypeError.cs | 2 +- .../Model/MonitorResult.cs | 2 +- .../Model/MonitoringLogResponse.cs | 2 +- .../Model/MonitoringProbeType.cs | 2 +- .../Model/MonitoringProbeTypeOneOf.cs | 2 +- .../Model/MonitorsConfig.cs | 2 +- .../Model/NetworkInterfaceDto.cs | 2 +- .../Model/NetworkInterfacesResponse.cs | 2 +- .../Model/NetworkScanResultEventDto.cs | 2 +- .../Model/NetworkScanResultKindDto.cs | 2 +- .../Model/NetworkScanSourceCapabilitiesDto.cs | 2 +- .../Model/NetworkScanSourceDto.cs | 2 +- .../Model/PreflightAlertStatus.cs | 2 +- .../Model/PreflightOperation.cs | 2 +- .../Model/PreflightOperationKind.cs | 2 +- .../Model/PreflightOutput.cs | 2 +- .../Model/PreflightOutputKind.cs | 2 +- .../Model/PubKeyFormat.cs | 2 +- .../Model/ScanOriginDto.cs | 2 +- .../Model/ScanResultSourceDto.cs | 2 +- .../Model/SessionInfo.cs | 2 +- .../Model/SessionTokenContentType.cs | 2 +- .../Model/SessionTokenSignRequest.cs | 2 +- .../Model/SetConfigResponse.cs | 2 +- .../Model/SetUpdateScheduleRequest.cs | 2 +- .../Model/SubProvisionerKey.cs | 2 +- .../Model/Subscriber.cs | 2 +- .../Model/TargetConnectionOptions.cs | 2 +- .../Model/TrafficEventResponse.cs | 2 +- .../Model/TransportProtocolResponse.cs | 2 +- .../Model/UpdateProductInfo.cs | 2 +- .../Model/UpdateRequestSchema.cs | 2 +- devolutions-gateway/openapi/gateway-api.yaml | 524 +++++++++++++++++- .../.openapi-generator/FILES | 2 + .../openapi/ts-angular-client/README.md | 2 +- .../ts-angular-client/api/agent.service.ts | 44 +- .../ts-angular-client/api/config.service.ts | 11 +- .../api/diagnostics.service.ts | 13 +- .../api/heartbeat.service.ts | 13 +- .../ts-angular-client/api/jrec.service.ts | 48 +- .../ts-angular-client/api/jrl.service.ts | 22 +- .../ts-angular-client/api/net.service.ts | 33 +- .../api/networkMonitoring.service.ts | 24 +- .../api/preflight.service.ts | 13 +- .../ts-angular-client/api/sessions.service.ts | 22 +- .../ts-angular-client/api/traffic.service.ts | 24 +- .../ts-angular-client/api/update.service.ts | 46 +- .../ts-angular-client/model/apiError.ts | 33 ++ .../ts-angular-client/model/errorCode.ts | 43 ++ .../openapi/ts-angular-client/model/models.ts | 2 + 131 files changed, 1696 insertions(+), 405 deletions(-) create mode 100644 devolutions-gateway/openapi/dotnet-client/docs/ApiError.md create mode 100644 devolutions-gateway/openapi/dotnet-client/docs/ErrorCode.md create mode 100644 devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ApiError.cs create mode 100644 devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ErrorCode.cs create mode 100644 devolutions-gateway/openapi/ts-angular-client/model/apiError.ts create mode 100644 devolutions-gateway/openapi/ts-angular-client/model/errorCode.ts diff --git a/devolutions-gateway/openapi/doc/index.adoc b/devolutions-gateway/openapi/doc/index.adoc index 04e0934d0f..7db5b873c3 100644 --- a/devolutions-gateway/openapi/doc/index.adoc +++ b/devolutions-gateway/openapi/doc/index.adoc @@ -1,6 +1,6 @@ = devolutions-gateway infos@devolutions.net -2026.2.4 +2026.3.2 :toc: left :numbered: :toclevels: 4 @@ -120,6 +120,9 @@ Delete an accepted agent by ID. - +===== Content Type + +* application/problem+json ===== Responses @@ -136,22 +139,22 @@ Delete an accepted agent by ID. | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 404 | Agent not found -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -221,6 +224,7 @@ Requires a Bearer token: an `ENROLLMENT` JWT signed by the configured provisione ===== Content Type * application/json +* application/problem+json ===== Responses @@ -237,22 +241,22 @@ Requires a Bearer token: an `ENROLLMENT` JWT signed by the configured provisione | 400 | Invalid agent name, request body, or certificate signing request -| <<>> +| <> | 401 | Invalid or missing enrollment token -| <<>> +| <> | 409 | Agent ID or name already registered, agent key previously deleted, or enrollment token already used -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -322,6 +326,7 @@ Get a single agent by ID. ===== Content Type * application/json +* application/problem+json ===== Responses @@ -338,22 +343,22 @@ Get a single agent by ID. | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 404 | Agent not found -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -403,6 +408,7 @@ array[<>] ===== Content Type * application/json +* application/problem+json ===== Responses @@ -419,17 +425,17 @@ array[<>] | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -501,6 +507,9 @@ Modifies configuration - +===== Content Type + +* application/problem+json ===== Responses @@ -517,22 +526,22 @@ Modifies configuration | 400 | Bad patch request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to patch configuration -| <<>> +| <> |=== @@ -640,6 +649,7 @@ This route primary function is to help with configuration diagnosis (e.g.: ID mi ===== Content Type * application/json +* application/problem+json ===== Responses @@ -656,17 +666,17 @@ This route primary function is to help with configuration diagnosis (e.g.: ID mi | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> |=== @@ -865,6 +875,7 @@ Performs a heartbeat check ===== Content Type * application/json +* application/problem+json ===== Responses @@ -881,17 +892,17 @@ Performs a heartbeat check | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> |=== @@ -965,6 +976,7 @@ If you try to delete more than 50,000 recordings at once, you should split the l ===== Content Type * application/json +* application/problem+json ===== Responses @@ -981,27 +993,27 @@ If you try to delete more than 50,000 recordings at once, you should split the l | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 409 | A recording is still ongoing and can't be deleted yet (nothing is deleted) -| <<>> +| <> | 413 | Request payload is too large -| <<>> +| <> |=== @@ -1069,6 +1081,9 @@ Deletes a recording stored on this instance - +===== Content Type + +* application/problem+json ===== Responses @@ -1085,27 +1100,27 @@ Deletes a recording stored on this instance | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 404 | The specified recording was not found -| <<>> +| <> | 409 | The recording is still ongoing and can't be deleted yet -| <<>> +| <> |=== @@ -1176,6 +1191,7 @@ Lists all recordings stored on this instance ===== Content Type * application/json +* application/problem+json ===== Responses @@ -1192,17 +1208,17 @@ Lists all recordings stored on this instance | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> |=== @@ -1278,7 +1294,12 @@ Retrieves a recording file for a given session ===== Content Type +* video/webm +* application/x-asciicast +* application/x-ndjson +* application/json * application/octet-stream +* application/problem+json ===== Responses @@ -1295,22 +1316,22 @@ Retrieves a recording file for a given session | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 404 | File not found -| <<>> +| <> |=== @@ -1466,6 +1487,7 @@ Retrieves current JRL (Json Revocation List) info ===== Content Type * application/json +* application/problem+json ===== Responses @@ -1482,22 +1504,22 @@ Retrieves current JRL (Json Revocation List) info | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to update the JRL -| <<>> +| <> |=== @@ -1545,6 +1567,9 @@ Updates JRL (Json Revocation List) using a JRL token - +===== Content Type + +* application/problem+json ===== Responses @@ -1561,22 +1586,22 @@ Updates JRL (Json Revocation List) using a JRL token | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to update the JRL -| <<>> +| <> |=== @@ -1631,6 +1656,7 @@ Lists network interfaces ===== Content Type * application/json +* application/problem+json ===== Responses @@ -1647,22 +1673,22 @@ Lists network interfaces | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -1712,6 +1738,7 @@ Lists Gateway network scan sources. ===== Content Type * application/json +* application/problem+json ===== Responses @@ -1728,22 +1755,22 @@ Lists Gateway network scan sources. | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -1997,6 +2024,9 @@ The endpoint is upgraded from HTTP, so OpenAPI describes the **handshake**: the - +===== Content Type + +* application/problem+json ===== Responses @@ -2018,17 +2048,17 @@ The endpoint is upgraded from HTTP, so OpenAPI describes the **handshake**: the | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -2082,6 +2112,7 @@ Once the log is downloaded, gateway purges it from memory. ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2098,22 +2129,22 @@ Once the log is downloaded, gateway purges it from memory. | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -2183,6 +2214,7 @@ Changes take effect immediately: - Starts any monitors newly defined in the payl ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2199,22 +2231,22 @@ Changes take effect immediately: - Starts any monitors newly defined in the payl | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error while starting monitors -| <<>> +| <> |=== @@ -2288,6 +2320,7 @@ array[<>] ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2304,17 +2337,17 @@ array[<>] | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> |=== @@ -2368,6 +2401,7 @@ array[<>] ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2384,22 +2418,22 @@ array[<>] | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -2467,6 +2501,9 @@ Terminate forcefully a running session - +===== Content Type + +* application/problem+json ===== Responses @@ -2483,27 +2520,27 @@ Terminate forcefully a running session | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 404 | No running session found with provided ID -| <<>> +| <> | 500 | Unexpected server error -| <<>> +| <> |=== @@ -2577,6 +2614,7 @@ Acknowledge traffic audit events and remove them from the queue ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2593,27 +2631,27 @@ Acknowledge traffic audit events and remove them from the queue | 400 | Invalid request body (empty ids array) -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 413 | Payload too large (more than 10,000 IDs) -| <<>> +| <> | 500 | Internal server error -| <<>> +| <> |=== @@ -2689,6 +2727,7 @@ array[<>] ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2705,22 +2744,22 @@ array[<>] | 400 | Invalid query parameters -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Internal server error -| <<>> +| <> |=== @@ -2774,6 +2813,7 @@ Reads `update_status.json`, which is written by the Devolutions Agent on startup ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2790,22 +2830,22 @@ Reads `update_status.json`, which is written by the Devolutions Agent on startup | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to read agent status file -| <<>> +| <> | 503 | Agent updater service is unavailable -| <<>> +| <> |=== @@ -2855,6 +2895,7 @@ Reads the `Schedule` field from `update_status.json`. When the field is absent ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2871,22 +2912,22 @@ Reads the `Schedule` field from `update_status.json`. When the field is absent | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to read agent status file -| <<>> +| <> | 503 | Agent updater service is unavailable -| <<>> +| <> |=== @@ -2957,6 +2998,7 @@ Writes the `Schedule` field into `update.json`. The agent watches this file and ===== Content Type * application/json +* application/problem+json ===== Responses @@ -2973,27 +3015,27 @@ Writes the `Schedule` field into `update.json`. The agent watches this file and | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to write update manifest -| <<>> +| <> | 503 | Agent updater service is unavailable -| <<>> +| <> |=== @@ -3077,6 +3119,7 @@ Writes the requested version(s) into `Agent/update.json`, which is watched by De ===== Content Type * application/json +* application/problem+json ===== Responses @@ -3093,27 +3136,27 @@ Writes the requested version(s) into `Agent/update.json`, which is watched by De | 400 | Bad request -| <<>> +| <> | 401 | Invalid or missing authorization token -| <<>> +| <> | 403 | Insufficient permissions -| <<>> +| <> | 500 | Failed to write update manifest -| <<>> +| <> | 503 | Agent updater service is unavailable -| <<>> +| <> |=== @@ -3544,6 +3587,51 @@ endif::internal-generation[] |=== +[#ApiError] +=== _ApiError_ + +Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). + +The Gateway logs the details of the error, along with its code. + + +[.fields-ApiError] +[cols="2,1,1,2,4,1"] +|=== +| Field Name| Required| Nullable | Type| Description | Format + +| error +| X +| +| String +| Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. +| + +| status +| X +| +| Integer +| HTTP status code. +| int32 + +| title +| X +| +| String +| Reason phrase of the HTTP status. +| + +| type +| X +| +| String +| Problem type, always `about:blank`: the HTTP status and the error code describe the problem. +| + +|=== + + + [#AppCredential] === _AppCredential_ @@ -4015,6 +4103,74 @@ Service configuration diagnostic +[#ErrorCode] +=== _ErrorCode_ + +Machine-readable reason for a rejected request. + +New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. + +- `bad_request`: The request is invalid. +- `unauthorized`: The request is not authenticated. +- `forbidden`: The request is not allowed. +- `not_found`: The resource doesn't exist. +- `request_timeout`: The request took too long to complete. +- `conflict`: The request conflicts with the current state of the resource. +- `payload_too_large`: The request is too large. +- `internal_error`: The Gateway failed to process the request. +- `bad_gateway`: The Gateway received an invalid response from an upstream server. +- `service_unavailable`: The service is temporarily unavailable. +- `gateway_timeout`: An upstream server didn't respond in time. +- `insufficient_storage`: The Gateway is out of storage space. +- `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. +- `malformed_token`: The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. +- `bad_signature`: The token signature doesn't match the provisioner key of the Gateway. +- `unknown_subkey`: The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. +- `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. +- `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. +- `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). +- `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. +- `unexpected_replay`: The token was already used, and can't be used again in this context. +- `invalid_token`: The token is rejected for a reason not covered by another code. +- `wrong_token_type`: The type of the token is not accepted by this route. +- `insufficient_scope`: The scope of the token doesn't grant access to this route. + + + + +[.fields-ErrorCode] +[cols="1"] +|=== +| Enum Values + +| bad_request +| unauthorized +| forbidden +| not_found +| request_timeout +| conflict +| payload_too_large +| internal_error +| bad_gateway +| service_unavailable +| gateway_timeout +| insufficient_storage +| missing_token +| malformed_token +| bad_signature +| unknown_subkey +| expired +| not_yet_valid +| gateway_id_mismatch +| revoked +| unexpected_replay +| invalid_token +| wrong_token_type +| insufficient_scope + +|=== + + [#EventOutcomeResponse] === _EventOutcomeResponse_ diff --git a/devolutions-gateway/openapi/dotnet-client/.openapi-generator/FILES b/devolutions-gateway/openapi/dotnet-client/.openapi-generator/FILES index b49a091545..e0a7af649f 100644 --- a/devolutions-gateway/openapi/dotnet-client/.openapi-generator/FILES +++ b/devolutions-gateway/openapi/dotnet-client/.openapi-generator/FILES @@ -9,6 +9,7 @@ docs/AgentApi.md docs/AgentDomainAdvertisement.md docs/AgentInfo.md docs/AgentStatus.md +docs/ApiError.md docs/AppCredential.md docs/AppCredentialKind.md docs/AppTokenContentType.md @@ -24,6 +25,7 @@ docs/DeleteManyResult.md docs/DiagnosticsApi.md docs/EnrollRequest.md docs/EnrollResponse.md +docs/ErrorCode.md docs/EventOutcomeResponse.md docs/GetUpdateProductsResponse.md docs/GetUpdateScheduleResponse.md @@ -117,6 +119,7 @@ src/Devolutions.Gateway.Client/Model/AddressFamily.cs src/Devolutions.Gateway.Client/Model/AgentDomainAdvertisement.cs src/Devolutions.Gateway.Client/Model/AgentInfo.cs src/Devolutions.Gateway.Client/Model/AgentStatus.cs +src/Devolutions.Gateway.Client/Model/ApiError.cs src/Devolutions.Gateway.Client/Model/AppCredential.cs src/Devolutions.Gateway.Client/Model/AppCredentialKind.cs src/Devolutions.Gateway.Client/Model/AppTokenContentType.cs @@ -130,6 +133,7 @@ src/Devolutions.Gateway.Client/Model/DataEncoding.cs src/Devolutions.Gateway.Client/Model/DeleteManyResult.cs src/Devolutions.Gateway.Client/Model/EnrollRequest.cs src/Devolutions.Gateway.Client/Model/EnrollResponse.cs +src/Devolutions.Gateway.Client/Model/ErrorCode.cs src/Devolutions.Gateway.Client/Model/EventOutcomeResponse.cs src/Devolutions.Gateway.Client/Model/GetUpdateProductsResponse.cs src/Devolutions.Gateway.Client/Model/GetUpdateScheduleResponse.cs diff --git a/devolutions-gateway/openapi/dotnet-client/README.md b/devolutions-gateway/openapi/dotnet-client/README.md index c60cd47e28..c584d831e1 100644 --- a/devolutions-gateway/openapi/dotnet-client/README.md +++ b/devolutions-gateway/openapi/dotnet-client/README.md @@ -4,7 +4,7 @@ Protocol-aware fine-grained relay server This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 2026.2.4 +- API version: 2026.3.2 - SDK version: 2026.9.2 - Generator version: 7.9.0 - Build package: org.openapitools.codegen.languages.CSharpClientCodegen @@ -186,6 +186,7 @@ Class | Method | HTTP request | Description - [Model.AgentDomainAdvertisement](docs/AgentDomainAdvertisement.md) - [Model.AgentInfo](docs/AgentInfo.md) - [Model.AgentStatus](docs/AgentStatus.md) + - [Model.ApiError](docs/ApiError.md) - [Model.AppCredential](docs/AppCredential.md) - [Model.AppCredentialKind](docs/AppCredentialKind.md) - [Model.AppTokenContentType](docs/AppTokenContentType.md) @@ -199,6 +200,7 @@ Class | Method | HTTP request | Description - [Model.DeleteManyResult](docs/DeleteManyResult.md) - [Model.EnrollRequest](docs/EnrollRequest.md) - [Model.EnrollResponse](docs/EnrollResponse.md) + - [Model.ErrorCode](docs/ErrorCode.md) - [Model.EventOutcomeResponse](docs/EventOutcomeResponse.md) - [Model.GetUpdateProductsResponse](docs/GetUpdateProductsResponse.md) - [Model.GetUpdateScheduleResponse](docs/GetUpdateScheduleResponse.md) diff --git a/devolutions-gateway/openapi/dotnet-client/docs/AgentApi.md b/devolutions-gateway/openapi/dotnet-client/docs/AgentApi.md index 5e75e46720..e7ec869033 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/AgentApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/AgentApi.md @@ -91,7 +91,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details @@ -193,7 +193,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -293,7 +293,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -388,7 +388,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/ApiError.md b/devolutions-gateway/openapi/dotnet-client/docs/ApiError.md new file mode 100644 index 0000000000..25055993cb --- /dev/null +++ b/devolutions-gateway/openapi/dotnet-client/docs/ApiError.md @@ -0,0 +1,14 @@ +# Devolutions.Gateway.Client.Model.ApiError +Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). The Gateway logs the details of the error, along with its code. + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Error** | **string** | Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. | +**Status** | **int** | HTTP status code. | +**Title** | **string** | Reason phrase of the HTTP status. | +**Type** | **string** | Problem type, always `about:blank`: the HTTP status and the error code describe the problem. | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/devolutions-gateway/openapi/dotnet-client/docs/ConfigApi.md b/devolutions-gateway/openapi/dotnet-client/docs/ConfigApi.md index 0a7817f46e..1bdb619124 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/ConfigApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/ConfigApi.md @@ -88,7 +88,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/DiagnosticsApi.md b/devolutions-gateway/openapi/dotnet-client/docs/DiagnosticsApi.md index 51c637037e..22cb4f8118 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/DiagnosticsApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/DiagnosticsApi.md @@ -181,7 +181,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/ErrorCode.md b/devolutions-gateway/openapi/dotnet-client/docs/ErrorCode.md new file mode 100644 index 0000000000..b8834e669e --- /dev/null +++ b/devolutions-gateway/openapi/dotnet-client/docs/ErrorCode.md @@ -0,0 +1,10 @@ +# Devolutions.Gateway.Client.Model.ErrorCode +Machine-readable reason for a rejected request. New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. - `bad_request`: The request is invalid. - `unauthorized`: The request is not authenticated. - `forbidden`: The request is not allowed. - `not_found`: The resource doesn't exist. - `request_timeout`: The request took too long to complete. - `conflict`: The request conflicts with the current state of the resource. - `payload_too_large`: The request is too large. - `internal_error`: The Gateway failed to process the request. - `bad_gateway`: The Gateway received an invalid response from an upstream server. - `service_unavailable`: The service is temporarily unavailable. - `gateway_timeout`: An upstream server didn't respond in time. - `insufficient_storage`: The Gateway is out of storage space. - `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. - `malformed_token`: The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. - `bad_signature`: The token signature doesn't match the provisioner key of the Gateway. - `unknown_subkey`: The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. - `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). - `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. - `unexpected_replay`: The token was already used, and can't be used again in this context. - `invalid_token`: The token is rejected for a reason not covered by another code. - `wrong_token_type`: The type of the token is not accepted by this route. - `insufficient_scope`: The scope of the token doesn't grant access to this route. + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/devolutions-gateway/openapi/dotnet-client/docs/HeartbeatApi.md b/devolutions-gateway/openapi/dotnet-client/docs/HeartbeatApi.md index 8440f0371b..de89d08792 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/HeartbeatApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/HeartbeatApi.md @@ -87,7 +87,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/JrecApi.md b/devolutions-gateway/openapi/dotnet-client/docs/JrecApi.md index 5777700acd..e7d1a295e9 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/JrecApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/JrecApi.md @@ -98,7 +98,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -195,7 +195,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details @@ -296,7 +296,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -397,7 +397,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/octet-stream + - **Accept**: video/webm, application/x-asciicast, application/x-ndjson, application/json, application/octet-stream, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/JrlApi.md b/devolutions-gateway/openapi/dotnet-client/docs/JrlApi.md index 3c3b1cac74..0aeea6d45a 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/JrlApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/JrlApi.md @@ -88,7 +88,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -179,7 +179,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/NetApi.md b/devolutions-gateway/openapi/dotnet-client/docs/NetApi.md index b3e5663e98..2d75b12869 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/NetApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/NetApi.md @@ -89,7 +89,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -184,7 +184,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -344,7 +344,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/NetworkMonitoringApi.md b/devolutions-gateway/openapi/dotnet-client/docs/NetworkMonitoringApi.md index 7f74cd0eb0..5afc845fe8 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/NetworkMonitoringApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/NetworkMonitoringApi.md @@ -90,7 +90,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -192,7 +192,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/PreflightApi.md b/devolutions-gateway/openapi/dotnet-client/docs/PreflightApi.md index cb51dd42ed..cb321fd47f 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/PreflightApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/PreflightApi.md @@ -92,7 +92,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/SessionsApi.md b/devolutions-gateway/openapi/dotnet-client/docs/SessionsApi.md index 4bb9e5ca97..86d3fc3b91 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/SessionsApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/SessionsApi.md @@ -88,7 +88,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -184,7 +184,7 @@ void (empty response body) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: Not defined + - **Accept**: application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/TrafficApi.md b/devolutions-gateway/openapi/dotnet-client/docs/TrafficApi.md index d5fb93fab0..8a8f6b2405 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/TrafficApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/TrafficApi.md @@ -93,7 +93,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -196,7 +196,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/docs/UpdateApi.md b/devolutions-gateway/openapi/dotnet-client/docs/UpdateApi.md index ffd85c00a5..e66a01e49f 100644 --- a/devolutions-gateway/openapi/dotnet-client/docs/UpdateApi.md +++ b/devolutions-gateway/openapi/dotnet-client/docs/UpdateApi.md @@ -92,7 +92,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -189,7 +189,7 @@ This endpoint does not need any parameter. ### HTTP request headers - **Content-Type**: Not defined - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -291,7 +291,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details @@ -396,7 +396,7 @@ catch (ApiException e) ### HTTP request headers - **Content-Type**: application/json - - **Accept**: application/json + - **Accept**: application/json, application/problem+json ### HTTP response details diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/AgentApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/AgentApi.cs index 928dd66bcf..eca70915a6 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/AgentApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/AgentApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -439,6 +439,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteAgentWithHttp // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -497,6 +498,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteAgentWithHttp // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; @@ -560,7 +562,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse EnrollAgent // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -625,7 +628,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse EnrollAgent // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -684,7 +688,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetAgentWithHttp // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -744,7 +749,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetAgentWithHttp // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -801,7 +807,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> ListAgents // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -858,7 +865,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> ListAgents // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/ConfigApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/ConfigApi.cs index 49012f143a..6db49f7bc4 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/ConfigApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/ConfigApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -322,6 +322,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse PatchConfigWithHttp // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -385,6 +386,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse PatchConfigWithHttp // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/DiagnosticsApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/DiagnosticsApi.cs index 9d95407159..73a5a54a94 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/DiagnosticsApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/DiagnosticsApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -493,7 +493,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetConfig // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -550,7 +551,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetConfig // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HealthApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HealthApi.cs index 328a0ba4a4..e11fc62732 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HealthApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HealthApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HeartbeatApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HeartbeatApi.cs index 5a5a673021..5e652cfb69 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HeartbeatApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/HeartbeatApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -312,7 +312,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetHeartbeatWith // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -369,7 +370,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetHeartbeatWith // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrecApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrecApi.cs index 17a031fcd6..77f03d70f7 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrecApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrecApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -497,7 +497,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteMan // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -562,7 +563,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteMan // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -620,6 +622,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteRecordingWith // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -678,6 +681,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse DeleteRecordingWith // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; @@ -736,7 +740,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> ListRecordingsW // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -796,7 +801,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> ListRecordingsW // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -861,7 +867,12 @@ public Devolutions.Gateway.Client.Client.ApiResponse PullRecordin // to determine the Accept header string[] _accepts = new string[] { - "application/octet-stream" + "video/webm", + "application/x-asciicast", + "application/x-ndjson", + "application/json", + "application/octet-stream", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -928,7 +939,12 @@ public Devolutions.Gateway.Client.Client.ApiResponse PullRecordin // to determine the Accept header string[] _accepts = new string[] { - "application/octet-stream" + "video/webm", + "application/x-asciicast", + "application/x-ndjson", + "application/json", + "application/octet-stream", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrlApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrlApi.cs index 79c8443e53..8aebaf350c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrlApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/JrlApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -349,7 +349,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetJrlInfoWithHttp // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -406,7 +407,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse GetJrlInfoWithHttp // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -461,6 +463,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse UpdateJrlWithHttpIn // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -516,6 +519,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse UpdateJrlWithHttpIn // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetApi.cs index 265d086e9f..b533ae2e7c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -517,7 +517,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -687,7 +690,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -806,6 +810,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -1053,6 +1058,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetworkMonitoringApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetworkMonitoringApi.cs index cb7415dc35..8e80c8de1e 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetworkMonitoringApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/NetworkMonitoringApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -359,7 +359,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse Drai // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -416,7 +417,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse Drai // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -479,7 +481,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetMonit // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -544,7 +547,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetMonit // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/PreflightApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/PreflightApi.cs index 03f20799bb..84298cca2b 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/PreflightApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/PreflightApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -323,7 +323,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> Post // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -388,7 +389,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> Post // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/SessionsApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/SessionsApi.cs index f08d09dc17..d41fd33524 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/SessionsApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/SessionsApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -353,7 +353,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> GetSessi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -410,7 +411,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> GetSessi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -467,6 +469,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse TerminateSessionWit // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -525,6 +528,7 @@ public Devolutions.Gateway.Client.Client.ApiResponse TerminateSessionWit // to determine the Accept header string[] _accepts = new string[] { + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/TrafficApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/TrafficApi.cs index 127ae622a0..4e225633ba 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/TrafficApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/TrafficApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -368,7 +368,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse AckTrafficEven // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -433,7 +434,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse AckTrafficEven // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -494,7 +496,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -557,7 +560,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse> // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/UpdateApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/UpdateApi.cs index 0eb72c1870..0e5b609d82 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/UpdateApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/UpdateApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -447,7 +447,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -504,7 +505,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -560,7 +562,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -617,7 +620,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -680,7 +684,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetUpdateScheduleWi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -745,7 +750,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetUpdateScheduleWi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; @@ -807,7 +813,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetUpdateScheduleWi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; var localVarContentType = Devolutions.Gateway.Client.Client.ClientUtils.SelectHeaderContentType(_contentTypes); @@ -874,7 +881,8 @@ public Devolutions.Gateway.Client.Client.ApiResponse SetUpdateScheduleWi // to determine the Accept header string[] _accepts = new string[] { - "application/json" + "application/json", + "application/problem+json" }; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/WebAppApi.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/WebAppApi.cs index ae2be8539d..53565ff6db 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/WebAppApi.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Api/WebAppApi.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiClient.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiClient.cs index 4d77365cc7..75881d7923 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiClient.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiClient.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiException.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiException.cs index 19461fa464..8a1d39e6be 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiException.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiException.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiResponse.cs index 955894c8f0..023111af95 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ApiResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ClientUtils.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ClientUtils.cs index 89ef69ff9d..9c956ca1eb 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ClientUtils.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ClientUtils.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Configuration.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Configuration.cs index ac253bf0b5..1c2310492c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Configuration.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Configuration.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ @@ -540,7 +540,7 @@ public static string ToDebugReport() string report = "C# SDK (Devolutions.Gateway.Client) Debug Report:\n"; report += " OS: " + System.Environment.OSVersion + "\n"; report += " .NET Framework Version: " + System.Environment.Version + "\n"; - report += " Version of the API: 2026.2.4\n"; + report += " Version of the API: 2026.3.2\n"; report += " SDK Package Version: 2026.9.2\n"; return report; diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ExceptionFactory.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ExceptionFactory.cs index 1e2698d5ad..18894b43b4 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ExceptionFactory.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ExceptionFactory.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/FileParameter.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/FileParameter.cs index f557e590dd..cb43f7fcc3 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/FileParameter.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/FileParameter.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/GlobalConfiguration.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/GlobalConfiguration.cs index 06ba08891f..51d7066959 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/GlobalConfiguration.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/GlobalConfiguration.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IApiAccessor.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IApiAccessor.cs index de69db1b16..833d8e3993 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IApiAccessor.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IApiAccessor.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IAsynchronousClient.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IAsynchronousClient.cs index 81c81f40c1..4c459814c9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IAsynchronousClient.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IAsynchronousClient.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IReadableConfiguration.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IReadableConfiguration.cs index 5ad5279b4b..f3a3c63b8b 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IReadableConfiguration.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/IReadableConfiguration.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ISynchronousClient.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ISynchronousClient.cs index 24fa46df0a..b78ae92c66 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ISynchronousClient.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/ISynchronousClient.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Multimap.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Multimap.cs index 6c6c6dd47b..c4fbe16aa4 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Multimap.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/Multimap.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/OpenAPIDateConverter.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/OpenAPIDateConverter.cs index a2c6d86ca3..b4c350d114 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/OpenAPIDateConverter.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/OpenAPIDateConverter.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RequestOptions.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RequestOptions.cs index 3d250a880c..3f394b6946 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RequestOptions.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RequestOptions.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RetryConfiguration.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RetryConfiguration.cs index 5a7dad3153..3df98b0581 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RetryConfiguration.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/RetryConfiguration.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/WebRequestPathBuilder.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/WebRequestPathBuilder.cs index 0246ad865a..724ed59c20 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/WebRequestPathBuilder.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Client/WebRequestPathBuilder.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AbstractOpenAPISchema.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AbstractOpenAPISchema.cs index f704f6a04a..445b9eb7e9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AbstractOpenAPISchema.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AbstractOpenAPISchema.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AccessScope.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AccessScope.cs index 98c7ac937d..d34fff5a51 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AccessScope.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AccessScope.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckRequest.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckRequest.cs index 453b72feab..69d533864f 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckRequest.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckRequest.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckResponse.cs index 217abd0693..7c8b27d11c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AckResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AddressFamily.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AddressFamily.cs index d288af632f..aad47a8b1e 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AddressFamily.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AddressFamily.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentDomainAdvertisement.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentDomainAdvertisement.cs index fe66c8372c..edb36d7929 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentDomainAdvertisement.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentDomainAdvertisement.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentInfo.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentInfo.cs index a4e1c16ff4..8c0e36de16 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentInfo.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentInfo.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentStatus.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentStatus.cs index dcbe4e2a2b..97ab06b884 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentStatus.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AgentStatus.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ApiError.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ApiError.cs new file mode 100644 index 0000000000..5dd4e93dbd --- /dev/null +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ApiError.cs @@ -0,0 +1,141 @@ +/* + * devolutions-gateway + * + * Protocol-aware fine-grained relay server + * + * The version of the OpenAPI document: 2026.3.2 + * Contact: infos@devolutions.net + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using FileParameter = Devolutions.Gateway.Client.Client.FileParameter; +using OpenAPIDateConverter = Devolutions.Gateway.Client.Client.OpenAPIDateConverter; + +namespace Devolutions.Gateway.Client.Model +{ + /// + /// Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). The Gateway logs the details of the error, along with its code. + /// + [DataContract(Name = "ApiError")] + public partial class ApiError : IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected ApiError() { } + /// + /// Initializes a new instance of the class. + /// + /// Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. (required). + /// HTTP status code. (required). + /// Reason phrase of the HTTP status. (required). + /// Problem type, always `about:blank`: the HTTP status and the error code describe the problem. (required). + public ApiError(string error = default(string), int status = default(int), string title = default(string), string type = default(string)) + { + // to ensure "error" is required (not null) + if (error == null) + { + throw new ArgumentNullException("error is a required property for ApiError and cannot be null"); + } + this.Error = error; + this.Status = status; + // to ensure "title" is required (not null) + if (title == null) + { + throw new ArgumentNullException("title is a required property for ApiError and cannot be null"); + } + this.Title = title; + // to ensure "type" is required (not null) + if (type == null) + { + throw new ArgumentNullException("type is a required property for ApiError and cannot be null"); + } + this.Type = type; + } + + /// + /// Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. + /// + /// Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. + [DataMember(Name = "error", IsRequired = true, EmitDefaultValue = true)] + public string Error { get; set; } + + /// + /// HTTP status code. + /// + /// HTTP status code. + [DataMember(Name = "status", IsRequired = true, EmitDefaultValue = true)] + public int Status { get; set; } + + /// + /// Reason phrase of the HTTP status. + /// + /// Reason phrase of the HTTP status. + [DataMember(Name = "title", IsRequired = true, EmitDefaultValue = true)] + public string Title { get; set; } + + /// + /// Problem type, always `about:blank`: the HTTP status and the error code describe the problem. + /// + /// Problem type, always `about:blank`: the HTTP status and the error code describe the problem. + [DataMember(Name = "type", IsRequired = true, EmitDefaultValue = true)] + public string Type { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class ApiError {\n"); + sb.Append(" Error: ").Append(Error).Append("\n"); + sb.Append(" Status: ").Append(Status).Append("\n"); + sb.Append(" Title: ").Append(Title).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + // Status (int) minimum + if (this.Status < (int)0) + { + yield return new ValidationResult("Invalid value for Status, must be a value greater than or equal to 0.", new [] { "Status" }); + } + + yield break; + } + } + +} diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredential.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredential.cs index 37d90237bb..105331a058 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredential.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredential.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredentialKind.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredentialKind.cs index c6bf83bbf6..d7a27f5ac0 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredentialKind.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppCredentialKind.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenContentType.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenContentType.cs index 50fcc49624..f172534bae 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenContentType.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenContentType.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenSignRequest.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenSignRequest.cs index fb54ffa0d5..8484bcb1b9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenSignRequest.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/AppTokenSignRequest.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClaimedTrafficEvent.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClaimedTrafficEvent.cs index a69d86bda9..83045e9253 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClaimedTrafficEvent.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClaimedTrafficEvent.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClockDiagnostic.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClockDiagnostic.cs index 119d37def9..5ad6296bb2 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClockDiagnostic.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ClockDiagnostic.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigDiagnostic.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigDiagnostic.cs index b5c6c4a98b..f6c17afeef 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigDiagnostic.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigDiagnostic.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigPatch.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigPatch.cs index b23cdace12..1364b7008c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigPatch.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConfigPatch.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConnectionMode.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConnectionMode.cs index 1b02f83831..080389576f 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConnectionMode.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ConnectionMode.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DataEncoding.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DataEncoding.cs index 43551f3c4a..e714568ddd 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DataEncoding.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DataEncoding.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DeleteManyResult.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DeleteManyResult.cs index d0c3420bcc..755edbb3f5 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DeleteManyResult.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/DeleteManyResult.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollRequest.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollRequest.cs index a8a4b507d4..e82024067d 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollRequest.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollRequest.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollResponse.cs index 00fd3ef98b..b006711615 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EnrollResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ErrorCode.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ErrorCode.cs new file mode 100644 index 0000000000..1decb09946 --- /dev/null +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ErrorCode.cs @@ -0,0 +1,245 @@ +/* + * devolutions-gateway + * + * Protocol-aware fine-grained relay server + * + * The version of the OpenAPI document: 2026.3.2 + * Contact: infos@devolutions.net + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using FileParameter = Devolutions.Gateway.Client.Client.FileParameter; +using OpenAPIDateConverter = Devolutions.Gateway.Client.Client.OpenAPIDateConverter; + +namespace Devolutions.Gateway.Client.Model +{ + /// + /// Machine-readable reason for a rejected request. New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. - `bad_request`: The request is invalid. - `unauthorized`: The request is not authenticated. - `forbidden`: The request is not allowed. - `not_found`: The resource doesn't exist. - `request_timeout`: The request took too long to complete. - `conflict`: The request conflicts with the current state of the resource. - `payload_too_large`: The request is too large. - `internal_error`: The Gateway failed to process the request. - `bad_gateway`: The Gateway received an invalid response from an upstream server. - `service_unavailable`: The service is temporarily unavailable. - `gateway_timeout`: An upstream server didn't respond in time. - `insufficient_storage`: The Gateway is out of storage space. - `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. - `malformed_token`: The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. - `bad_signature`: The token signature doesn't match the provisioner key of the Gateway. - `unknown_subkey`: The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. - `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). - `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. - `unexpected_replay`: The token was already used, and can't be used again in this context. - `invalid_token`: The token is rejected for a reason not covered by another code. - `wrong_token_type`: The type of the token is not accepted by this route. - `insufficient_scope`: The scope of the token doesn't grant access to this route. + /// + /// Machine-readable reason for a rejected request. New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. - `bad_request`: The request is invalid. - `unauthorized`: The request is not authenticated. - `forbidden`: The request is not allowed. - `not_found`: The resource doesn't exist. - `request_timeout`: The request took too long to complete. - `conflict`: The request conflicts with the current state of the resource. - `payload_too_large`: The request is too large. - `internal_error`: The Gateway failed to process the request. - `bad_gateway`: The Gateway received an invalid response from an upstream server. - `service_unavailable`: The service is temporarily unavailable. - `gateway_timeout`: An upstream server didn't respond in time. - `insufficient_storage`: The Gateway is out of storage space. - `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. - `malformed_token`: The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. - `bad_signature`: The token signature doesn't match the provisioner key of the Gateway. - `unknown_subkey`: The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. - `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). - `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. - `unexpected_replay`: The token was already used, and can't be used again in this context. - `invalid_token`: The token is rejected for a reason not covered by another code. - `wrong_token_type`: The type of the token is not accepted by this route. - `insufficient_scope`: The scope of the token doesn't grant access to this route. + [JsonConverter(typeof(StringEnumConverter))] + public enum ErrorCode + { + /// + /// Enum BadRequest for value: bad_request + /// + [EnumMember(Value = "bad_request")] + BadRequest = 1, + + /// + /// Enum Unauthorized for value: unauthorized + /// + [EnumMember(Value = "unauthorized")] + Unauthorized = 2, + + /// + /// Enum Forbidden for value: forbidden + /// + [EnumMember(Value = "forbidden")] + Forbidden = 3, + + /// + /// Enum NotFound for value: not_found + /// + [EnumMember(Value = "not_found")] + NotFound = 4, + + /// + /// Enum RequestTimeout for value: request_timeout + /// + [EnumMember(Value = "request_timeout")] + RequestTimeout = 5, + + /// + /// Enum Conflict for value: conflict + /// + [EnumMember(Value = "conflict")] + Conflict = 6, + + /// + /// Enum PayloadTooLarge for value: payload_too_large + /// + [EnumMember(Value = "payload_too_large")] + PayloadTooLarge = 7, + + /// + /// Enum InternalError for value: internal_error + /// + [EnumMember(Value = "internal_error")] + InternalError = 8, + + /// + /// Enum BadGateway for value: bad_gateway + /// + [EnumMember(Value = "bad_gateway")] + BadGateway = 9, + + /// + /// Enum ServiceUnavailable for value: service_unavailable + /// + [EnumMember(Value = "service_unavailable")] + ServiceUnavailable = 10, + + /// + /// Enum GatewayTimeout for value: gateway_timeout + /// + [EnumMember(Value = "gateway_timeout")] + GatewayTimeout = 11, + + /// + /// Enum InsufficientStorage for value: insufficient_storage + /// + [EnumMember(Value = "insufficient_storage")] + InsufficientStorage = 12, + + /// + /// Enum MissingToken for value: missing_token + /// + [EnumMember(Value = "missing_token")] + MissingToken = 13, + + /// + /// Enum MalformedToken for value: malformed_token + /// + [EnumMember(Value = "malformed_token")] + MalformedToken = 14, + + /// + /// Enum BadSignature for value: bad_signature + /// + [EnumMember(Value = "bad_signature")] + BadSignature = 15, + + /// + /// Enum UnknownSubkey for value: unknown_subkey + /// + [EnumMember(Value = "unknown_subkey")] + UnknownSubkey = 16, + + /// + /// Enum Expired for value: expired + /// + [EnumMember(Value = "expired")] + Expired = 17, + + /// + /// Enum NotYetValid for value: not_yet_valid + /// + [EnumMember(Value = "not_yet_valid")] + NotYetValid = 18, + + /// + /// Enum GatewayIdMismatch for value: gateway_id_mismatch + /// + [EnumMember(Value = "gateway_id_mismatch")] + GatewayIdMismatch = 19, + + /// + /// Enum Revoked for value: revoked + /// + [EnumMember(Value = "revoked")] + Revoked = 20, + + /// + /// Enum UnexpectedReplay for value: unexpected_replay + /// + [EnumMember(Value = "unexpected_replay")] + UnexpectedReplay = 21, + + /// + /// Enum InvalidToken for value: invalid_token + /// + [EnumMember(Value = "invalid_token")] + InvalidToken = 22, + + /// + /// Enum WrongTokenType for value: wrong_token_type + /// + [EnumMember(Value = "wrong_token_type")] + WrongTokenType = 23, + + /// + /// Enum InsufficientScope for value: insufficient_scope + /// + [EnumMember(Value = "insufficient_scope")] + InsufficientScope = 24 + } + + public static class ErrorCodeExtensions + { + /// + /// Returns the value as string for a given variant + /// + public static string ToValue(this ErrorCode variant) + { + switch (variant) + { + case ErrorCode.BadRequest: + return "bad_request"; + case ErrorCode.Unauthorized: + return "unauthorized"; + case ErrorCode.Forbidden: + return "forbidden"; + case ErrorCode.NotFound: + return "not_found"; + case ErrorCode.RequestTimeout: + return "request_timeout"; + case ErrorCode.Conflict: + return "conflict"; + case ErrorCode.PayloadTooLarge: + return "payload_too_large"; + case ErrorCode.InternalError: + return "internal_error"; + case ErrorCode.BadGateway: + return "bad_gateway"; + case ErrorCode.ServiceUnavailable: + return "service_unavailable"; + case ErrorCode.GatewayTimeout: + return "gateway_timeout"; + case ErrorCode.InsufficientStorage: + return "insufficient_storage"; + case ErrorCode.MissingToken: + return "missing_token"; + case ErrorCode.MalformedToken: + return "malformed_token"; + case ErrorCode.BadSignature: + return "bad_signature"; + case ErrorCode.UnknownSubkey: + return "unknown_subkey"; + case ErrorCode.Expired: + return "expired"; + case ErrorCode.NotYetValid: + return "not_yet_valid"; + case ErrorCode.GatewayIdMismatch: + return "gateway_id_mismatch"; + case ErrorCode.Revoked: + return "revoked"; + case ErrorCode.UnexpectedReplay: + return "unexpected_replay"; + case ErrorCode.InvalidToken: + return "invalid_token"; + case ErrorCode.WrongTokenType: + return "wrong_token_type"; + case ErrorCode.InsufficientScope: + return "insufficient_scope"; + default: + throw new ArgumentOutOfRangeException(nameof(variant), $"Unexpected variant: {variant}"); + } + } + } + +} diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EventOutcomeResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EventOutcomeResponse.cs index 25dd7a7c9b..37e7114012 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EventOutcomeResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/EventOutcomeResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateProductsResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateProductsResponse.cs index e45b5346a6..c53f9c92be 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateProductsResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateProductsResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateScheduleResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateScheduleResponse.cs index 214610d2c6..a223770756 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateScheduleResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/GetUpdateScheduleResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Heartbeat.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Heartbeat.cs index 70e8d5fced..8808060f9a 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Heartbeat.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Heartbeat.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/HostScanStateDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/HostScanStateDto.cs index a8773544a5..62b3017163 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/HostScanStateDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/HostScanStateDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Identity.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Identity.cs index f27bb51dcb..bf7f3c32a8 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Identity.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Identity.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/InterfaceInfo.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/InterfaceInfo.cs index 9acb50e903..56dc5c1fa9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/InterfaceInfo.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/InterfaceInfo.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/JrlInfo.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/JrlInfo.cs index 44ae29e6b2..e83fa902f2 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/JrlInfo.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/JrlInfo.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ListenerUrls.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ListenerUrls.cs index ccd051a68d..5e370db62c 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ListenerUrls.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ListenerUrls.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinition.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinition.cs index 1c5ced8dee..c9c03171d9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinition.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinition.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinitionProbeTypeError.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinitionProbeTypeError.cs index e6319f7fb2..b8219ca0a9 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinitionProbeTypeError.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorDefinitionProbeTypeError.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorResult.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorResult.cs index 3d326db439..523314c324 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorResult.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorResult.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringLogResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringLogResponse.cs index 5178361b79..e8a26b08fe 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringLogResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringLogResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeType.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeType.cs index d5940114cd..3365f6ba62 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeType.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeType.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeTypeOneOf.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeTypeOneOf.cs index c54f19afd5..6ff26dca83 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeTypeOneOf.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitoringProbeTypeOneOf.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorsConfig.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorsConfig.cs index e2023b36eb..187be86ced 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorsConfig.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/MonitorsConfig.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfaceDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfaceDto.cs index 82dd1c5eef..66762de14a 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfaceDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfaceDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfacesResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfacesResponse.cs index 35cdcf5e7e..e484236677 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfacesResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkInterfacesResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultEventDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultEventDto.cs index b55c29445f..1c7903a3e8 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultEventDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultEventDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultKindDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultKindDto.cs index a06d6f44a7..54f04d8092 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultKindDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanResultKindDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceCapabilitiesDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceCapabilitiesDto.cs index 5a67d2b4ff..897d7d1635 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceCapabilitiesDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceCapabilitiesDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceDto.cs index 5c10a9c6d8..44820c7bc1 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/NetworkScanSourceDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightAlertStatus.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightAlertStatus.cs index f943d4fb6f..5369584514 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightAlertStatus.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightAlertStatus.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperation.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperation.cs index e0fbcd5730..6b9fdfda75 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperation.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperation.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperationKind.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperationKind.cs index 7ec380fdd2..4500e5291d 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperationKind.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOperationKind.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutput.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutput.cs index 35b5cd0576..8dbf00a03a 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutput.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutput.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutputKind.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutputKind.cs index b05ad143ec..a0f3252965 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutputKind.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PreflightOutputKind.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PubKeyFormat.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PubKeyFormat.cs index 805d381c3d..f3189791b2 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PubKeyFormat.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/PubKeyFormat.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanOriginDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanOriginDto.cs index a47ef1ba34..70a42b7e30 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanOriginDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanOriginDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanResultSourceDto.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanResultSourceDto.cs index a98ab0a980..1a0033843e 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanResultSourceDto.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/ScanResultSourceDto.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionInfo.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionInfo.cs index a630216192..634841bbea 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionInfo.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionInfo.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenContentType.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenContentType.cs index 20b9a17735..a0600400bd 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenContentType.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenContentType.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenSignRequest.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenSignRequest.cs index 611233691a..e2ab13c023 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenSignRequest.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SessionTokenSignRequest.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetConfigResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetConfigResponse.cs index 1914cf188f..4584faced5 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetConfigResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetConfigResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetUpdateScheduleRequest.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetUpdateScheduleRequest.cs index e12658b419..c855c51822 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetUpdateScheduleRequest.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SetUpdateScheduleRequest.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SubProvisionerKey.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SubProvisionerKey.cs index 404ab0fed4..57c214f9e7 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SubProvisionerKey.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/SubProvisionerKey.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Subscriber.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Subscriber.cs index ac570f1477..0bc03c3b93 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Subscriber.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/Subscriber.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TargetConnectionOptions.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TargetConnectionOptions.cs index 8e7e742d09..05d5e9b248 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TargetConnectionOptions.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TargetConnectionOptions.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TrafficEventResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TrafficEventResponse.cs index 317a12c356..faaa969887 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TrafficEventResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TrafficEventResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TransportProtocolResponse.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TransportProtocolResponse.cs index dc275b1a81..8e93e3f5a3 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TransportProtocolResponse.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/TransportProtocolResponse.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateProductInfo.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateProductInfo.cs index 30008605cb..ad7dbb7e3d 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateProductInfo.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateProductInfo.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateRequestSchema.cs b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateRequestSchema.cs index 8adad30c17..ff1c6e4b6d 100644 --- a/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateRequestSchema.cs +++ b/devolutions-gateway/openapi/dotnet-client/src/Devolutions.Gateway.Client/Model/UpdateRequestSchema.cs @@ -3,7 +3,7 @@ * * Protocol-aware fine-grained relay server * - * The version of the OpenAPI document: 2026.2.4 + * The version of the OpenAPI document: 2026.3.2 * Contact: infos@devolutions.net * Generated by: https://github.com/openapitools/openapi-generator.git */ diff --git a/devolutions-gateway/openapi/gateway-api.yaml b/devolutions-gateway/openapi/gateway-api.yaml index a2c1b1b2b5..3a302f84f4 100644 --- a/devolutions-gateway/openapi/gateway-api.yaml +++ b/devolutions-gateway/openapi/gateway-api.yaml @@ -7,7 +7,7 @@ info: email: infos@devolutions.net license: name: MIT/Apache-2.0 - version: 2026.2.4 + version: 2026.3.2 paths: /jet/config: patch: @@ -27,12 +27,28 @@ paths: description: Configuration has been patched with success '400': description: Bad patch request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to patch configuration + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.config.write @@ -76,10 +92,22 @@ paths: $ref: '#/components/schemas/ConfigDiagnostic' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.diagnostics.read @@ -137,10 +165,22 @@ paths: $ref: '#/components/schemas/Heartbeat' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.heartbeat.read @@ -182,14 +222,34 @@ paths: $ref: '#/components/schemas/DeleteManyResult' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '409': description: A recording is still ongoing and can't be deleted yet (nothing is deleted) + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '413': description: Request payload is too large + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.recording.delete @@ -212,14 +272,34 @@ paths: description: Recording matching the ID in the path has been deleted '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '404': description: The specified recording was not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '409': description: The recording is still ongoing and can't be deleted yet + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.recording.delete @@ -248,10 +328,22 @@ paths: format: uuid '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.recordings.read @@ -337,12 +429,28 @@ paths: format: binary '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '404': description: File not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - jrec_token: - pull @@ -357,12 +465,28 @@ paths: description: JRL updated successfully '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to update the JRL + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - jrl_token: [] /jet/jrl/info: @@ -380,12 +504,28 @@ paths: $ref: '#/components/schemas/JrlInfo' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to update the JRL + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.jrl.read @@ -410,12 +550,28 @@ paths: $ref: '#/components/schemas/InterfaceInfo' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - netscan_token: [] /jet/net/interfaces: @@ -433,12 +589,28 @@ paths: $ref: '#/components/schemas/NetworkInterfacesResponse' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - netscan_token: [] /jet/net/monitor/config: @@ -472,12 +644,28 @@ paths: $ref: '#/components/schemas/SetConfigResponse' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error while starting monitors + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.net.monitor.config @@ -497,12 +685,28 @@ paths: $ref: '#/components/schemas/MonitoringLogResponse' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.net.monitor.drain @@ -805,10 +1009,22 @@ paths: description: Invalid query, mixed target/range, oversized range, or selected interface error '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - netscan_token: [] /jet/preflight: @@ -836,10 +1052,22 @@ paths: $ref: '#/components/schemas/PreflightOutput' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.preflight @@ -862,14 +1090,34 @@ paths: description: Session terminated successfully '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '404': description: No running session found with provided ID + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.session.terminate @@ -890,12 +1138,28 @@ paths: $ref: '#/components/schemas/SessionInfo' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.sessions.read @@ -921,14 +1185,34 @@ paths: $ref: '#/components/schemas/AckResponse' '400': description: Invalid request body (empty ids array) + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '413': description: Payload too large (more than 10,000 IDs) + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Internal server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.traffic.ack @@ -965,12 +1249,28 @@ paths: $ref: '#/components/schemas/ClaimedTrafficEvent' '400': description: Invalid query parameters + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Internal server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.traffic.claim @@ -991,10 +1291,22 @@ paths: $ref: '#/components/schemas/AgentInfo' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.agent.read @@ -1021,12 +1333,28 @@ paths: $ref: '#/components/schemas/AgentInfo' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '404': description: Agent not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.agent.read @@ -1048,12 +1376,28 @@ paths: description: Agent deleted '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '404': description: Agent not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.agent.delete @@ -1085,12 +1429,28 @@ paths: $ref: '#/components/schemas/EnrollResponse' '400': description: Invalid agent name, request body, or certificate signing request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing enrollment token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '409': description: Agent ID or name already registered, agent key previously deleted, or enrollment token already used + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Unexpected server error + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - enrollment_token: [] /jet/update: @@ -1113,12 +1473,28 @@ paths: $ref: '#/components/schemas/GetUpdateProductsResponse' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to read agent status file + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '503': description: Agent updater service is unavailable + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.update.read @@ -1165,14 +1541,34 @@ paths: $ref: '#/components/schemas/UpdateResponse' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to write update manifest + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '503': description: Agent updater service is unavailable + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.update @@ -1194,12 +1590,28 @@ paths: $ref: '#/components/schemas/GetUpdateScheduleResponse' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to read agent status file + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '503': description: Agent updater service is unavailable + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.update.read @@ -1230,14 +1642,34 @@ paths: $ref: '#/components/schemas/SetUpdateScheduleResponse' '400': description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '401': description: Invalid or missing authorization token + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '403': description: Insufficient permissions + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '500': description: Failed to write update manifest + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' '503': description: Agent updater service is unavailable + content: + application/problem+json: + schema: + $ref: '#/components/schemas/ApiError' security: - scope_token: - gateway.update @@ -1402,6 +1834,40 @@ components: - offline - online - unresponsive + ApiError: + type: object + description: |- + Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). + + The Gateway logs the details of the error, along with its code. + required: + - type + - title + - status + - error + properties: + error: + type: string + description: |- + Machine-readable error code; the `ErrorCode` schema lists the known values. + + New codes can be added: treat unknown values as generic failures. + status: + type: integer + format: int32 + description: HTTP status code. + minimum: 0 + title: + type: string + description: Reason phrase of the HTTP status. + type: + type: string + description: 'Problem type, always `about:blank`: the HTTP status and the error code describe the problem.' + example: + error: gateway_id_mismatch + status: 401 + title: Unauthorized + type: about:blank AppCredential: type: object required: @@ -1587,6 +2053,62 @@ components: description: |- SHA-256 hash of the server certificate's SPKI (hex-encoded). Used by the agent to pin the server's public key. + ErrorCode: + type: string + description: |- + Machine-readable reason for a rejected request. + + New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. + + - `bad_request`: The request is invalid. + - `unauthorized`: The request is not authenticated. + - `forbidden`: The request is not allowed. + - `not_found`: The resource doesn't exist. + - `request_timeout`: The request took too long to complete. + - `conflict`: The request conflicts with the current state of the resource. + - `payload_too_large`: The request is too large. + - `internal_error`: The Gateway failed to process the request. + - `bad_gateway`: The Gateway received an invalid response from an upstream server. + - `service_unavailable`: The service is temporarily unavailable. + - `gateway_timeout`: An upstream server didn't respond in time. + - `insufficient_storage`: The Gateway is out of storage space. + - `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. + - `malformed_token`: The token can't be decoded or decrypted, or its claims don't match the schema expected for its type. + - `bad_signature`: The token signature doesn't match the provisioner key of the Gateway. + - `unknown_subkey`: The token refers to a key ID (`kid`) that doesn't match the sub provisioner key of the Gateway. + - `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. + - `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. + - `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). + - `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. + - `unexpected_replay`: The token was already used, and can't be used again in this context. + - `invalid_token`: The token is rejected for a reason not covered by another code. + - `wrong_token_type`: The type of the token is not accepted by this route. + - `insufficient_scope`: The scope of the token doesn't grant access to this route. + enum: + - bad_request + - unauthorized + - forbidden + - not_found + - request_timeout + - conflict + - payload_too_large + - internal_error + - bad_gateway + - service_unavailable + - gateway_timeout + - insufficient_storage + - missing_token + - malformed_token + - bad_signature + - unknown_subkey + - expired + - not_yet_valid + - gateway_id_mismatch + - revoked + - unexpected_replay + - invalid_token + - wrong_token_type + - insufficient_scope EventOutcomeResponse: type: string enum: diff --git a/devolutions-gateway/openapi/ts-angular-client/.openapi-generator/FILES b/devolutions-gateway/openapi/ts-angular-client/.openapi-generator/FILES index 84eba61edd..bc364312b7 100644 --- a/devolutions-gateway/openapi/ts-angular-client/.openapi-generator/FILES +++ b/devolutions-gateway/openapi/ts-angular-client/.openapi-generator/FILES @@ -26,6 +26,7 @@ model/addressFamily.ts model/agentDomainAdvertisement.ts model/agentInfo.ts model/agentStatus.ts +model/apiError.ts model/appCredential.ts model/appCredentialKind.ts model/appTokenContentType.ts @@ -39,6 +40,7 @@ model/dataEncoding.ts model/deleteManyResult.ts model/enrollRequest.ts model/enrollResponse.ts +model/errorCode.ts model/eventOutcomeResponse.ts model/getUpdateProductsResponse.ts model/getUpdateScheduleResponse.ts diff --git a/devolutions-gateway/openapi/ts-angular-client/README.md b/devolutions-gateway/openapi/ts-angular-client/README.md index 87153eb065..ab89839999 100644 --- a/devolutions-gateway/openapi/ts-angular-client/README.md +++ b/devolutions-gateway/openapi/ts-angular-client/README.md @@ -2,7 +2,7 @@ Protocol-aware fine-grained relay server -The version of the OpenAPI document: 2026.2.4 +The version of the OpenAPI document: 2026.3.2 ### Building diff --git a/devolutions-gateway/openapi/ts-angular-client/api/agent.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/agent.service.ts index 04ed13cdab..3ac36bd4e9 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/agent.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/agent.service.ts @@ -19,6 +19,8 @@ import { Observable } from 'rxjs'; // @ts-ignore import { AgentInfo } from '../model/agentInfo'; // @ts-ignore +import { ApiError } from '../model/apiError'; +// @ts-ignore import { EnrollRequest } from '../model/enrollRequest'; // @ts-ignore import { EnrollResponse } from '../model/enrollResponse'; @@ -100,10 +102,10 @@ export class AgentService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public deleteAgent(agentId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public deleteAgent(agentId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteAgent(agentId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteAgent(agentId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public deleteAgent(agentId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public deleteAgent(agentId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteAgent(agentId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteAgent(agentId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (agentId === null || agentId === undefined) { throw new Error('Required parameter agentId was null or undefined when calling deleteAgent.'); } @@ -121,6 +123,7 @@ export class AgentService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -171,10 +174,10 @@ export class AgentService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public enrollAgent(enrollRequest: EnrollRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public enrollAgent(enrollRequest: EnrollRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public enrollAgent(enrollRequest: EnrollRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public enrollAgent(enrollRequest: EnrollRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public enrollAgent(enrollRequest: EnrollRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public enrollAgent(enrollRequest: EnrollRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public enrollAgent(enrollRequest: EnrollRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public enrollAgent(enrollRequest: EnrollRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (enrollRequest === null || enrollRequest === undefined) { throw new Error('Required parameter enrollRequest was null or undefined when calling enrollAgent.'); } @@ -192,7 +195,8 @@ export class AgentService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -252,10 +256,10 @@ export class AgentService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getAgent(agentId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getAgent(agentId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getAgent(agentId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getAgent(agentId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getAgent(agentId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getAgent(agentId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getAgent(agentId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getAgent(agentId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (agentId === null || agentId === undefined) { throw new Error('Required parameter agentId was null or undefined when calling getAgent.'); } @@ -273,7 +277,8 @@ export class AgentService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -322,10 +327,10 @@ export class AgentService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public listAgents(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public listAgents(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public listAgents(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public listAgents(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public listAgents(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public listAgents(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public listAgents(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public listAgents(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -340,7 +345,8 @@ export class AgentService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/config.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/config.service.ts index f2586e77e4..2f8506930b 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/config.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/config.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { ConfigPatch } from '../model/configPatch'; @@ -96,10 +98,10 @@ export class ConfigService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public patchConfig(configPatch: ConfigPatch, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public patchConfig(configPatch: ConfigPatch, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public patchConfig(configPatch: ConfigPatch, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public patchConfig(configPatch: ConfigPatch, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public patchConfig(configPatch: ConfigPatch, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public patchConfig(configPatch: ConfigPatch, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public patchConfig(configPatch: ConfigPatch, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public patchConfig(configPatch: ConfigPatch, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (configPatch === null || configPatch === undefined) { throw new Error('Required parameter configPatch was null or undefined when calling patchConfig.'); } @@ -117,6 +119,7 @@ export class ConfigService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/diagnostics.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/diagnostics.service.ts index 72b1d8da38..4147c8734a 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/diagnostics.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/diagnostics.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { ClockDiagnostic } from '../model/clockDiagnostic'; // @ts-ignore @@ -159,10 +161,10 @@ export class DiagnosticsService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getConfigurationDiagnostic(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getConfigurationDiagnostic(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getConfigurationDiagnostic(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getConfigurationDiagnostic(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getConfigurationDiagnostic(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getConfigurationDiagnostic(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getConfigurationDiagnostic(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getConfigurationDiagnostic(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -177,7 +179,8 @@ export class DiagnosticsService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/heartbeat.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/heartbeat.service.ts index 26040f8632..73a5702c3f 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/heartbeat.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/heartbeat.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { Heartbeat } from '../model/heartbeat'; @@ -95,10 +97,10 @@ export class HeartbeatService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getHeartbeat(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getHeartbeat(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getHeartbeat(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getHeartbeat(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getHeartbeat(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getHeartbeat(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getHeartbeat(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getHeartbeat(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -113,7 +115,8 @@ export class HeartbeatService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/jrec.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/jrec.service.ts index b1e8e0a18a..32ff1527b7 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/jrec.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/jrec.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { DeleteManyResult } from '../model/deleteManyResult'; @@ -97,10 +99,10 @@ export class JrecService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public deleteManyRecordings(requestBody: Array, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public deleteManyRecordings(requestBody: Array, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteManyRecordings(requestBody: Array, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteManyRecordings(requestBody: Array, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public deleteManyRecordings(requestBody: Array, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public deleteManyRecordings(requestBody: Array, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteManyRecordings(requestBody: Array, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteManyRecordings(requestBody: Array, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (requestBody === null || requestBody === undefined) { throw new Error('Required parameter requestBody was null or undefined when calling deleteManyRecordings.'); } @@ -118,7 +120,8 @@ export class JrecService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -178,10 +181,10 @@ export class JrecService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public deleteRecording(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public deleteRecording(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteRecording(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public deleteRecording(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public deleteRecording(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public deleteRecording(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteRecording(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public deleteRecording(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (id === null || id === undefined) { throw new Error('Required parameter id was null or undefined when calling deleteRecording.'); } @@ -199,6 +202,7 @@ export class JrecService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -248,10 +252,10 @@ export class JrecService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public listRecordings(active: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public listRecordings(active: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public listRecordings(active: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public listRecordings(active: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public listRecordings(active: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public listRecordings(active: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public listRecordings(active: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public listRecordings(active: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (active === null || active === undefined) { throw new Error('Required parameter active was null or undefined when calling listRecordings.'); } @@ -275,7 +279,8 @@ export class JrecService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -327,10 +332,10 @@ export class JrecService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public pullRecordingFile(id: string, filename: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/octet-stream', context?: HttpContext, transferCache?: boolean}): Observable; - public pullRecordingFile(id: string, filename: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/octet-stream', context?: HttpContext, transferCache?: boolean}): Observable>; - public pullRecordingFile(id: string, filename: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/octet-stream', context?: HttpContext, transferCache?: boolean}): Observable>; - public pullRecordingFile(id: string, filename: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/octet-stream', context?: HttpContext, transferCache?: boolean}): Observable { + public pullRecordingFile(id: string, filename: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'video/webm' | 'application/x-asciicast' | 'application/x-ndjson' | 'application/json' | 'application/octet-stream' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public pullRecordingFile(id: string, filename: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'video/webm' | 'application/x-asciicast' | 'application/x-ndjson' | 'application/json' | 'application/octet-stream' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public pullRecordingFile(id: string, filename: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'video/webm' | 'application/x-asciicast' | 'application/x-ndjson' | 'application/json' | 'application/octet-stream' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public pullRecordingFile(id: string, filename: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'video/webm' | 'application/x-asciicast' | 'application/x-ndjson' | 'application/json' | 'application/octet-stream' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (id === null || id === undefined) { throw new Error('Required parameter id was null or undefined when calling pullRecordingFile.'); } @@ -351,7 +356,12 @@ export class JrecService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/octet-stream' + 'video/webm', + 'application/x-asciicast', + 'application/x-ndjson', + 'application/json', + 'application/octet-stream', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/jrl.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/jrl.service.ts index 4063f75ae1..76c54d5eb5 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/jrl.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/jrl.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { JrlInfo } from '../model/jrlInfo'; @@ -95,10 +97,10 @@ export class JrlService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getJrlInfo(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getJrlInfo(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getJrlInfo(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getJrlInfo(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getJrlInfo(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getJrlInfo(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getJrlInfo(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getJrlInfo(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -113,7 +115,8 @@ export class JrlService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -162,10 +165,10 @@ export class JrlService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public updateJrl(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public updateJrl(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public updateJrl(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public updateJrl(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public updateJrl(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public updateJrl(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public updateJrl(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public updateJrl(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -180,6 +183,7 @@ export class JrlService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/net.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/net.service.ts index f34155875c..308b1ed899 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/net.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/net.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { InterfaceInfo } from '../model/interfaceInfo'; // @ts-ignore @@ -97,10 +99,10 @@ export class NetService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getNetConfig(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; }>>; - public getNetConfig(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; }>>>; - public getNetConfig(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; }>>>; - public getNetConfig(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getNetConfig(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; }>>; + public getNetConfig(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; }>>>; + public getNetConfig(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; }>>>; + public getNetConfig(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -115,7 +117,8 @@ export class NetService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -164,10 +167,10 @@ export class NetService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getNetInterfaces(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getNetInterfaces(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getNetInterfaces(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getNetInterfaces(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getNetInterfaces(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getNetInterfaces(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getNetInterfaces(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getNetInterfaces(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -182,7 +185,8 @@ export class NetService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -264,10 +268,10 @@ export class NetService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getNetScan(pingInterval?: number, pingTimeout?: number, broadcastTimeout?: number, portScanTimeout?: number, netbiosTimeout?: number, netbiosInterval?: number, mdnsQueryTimeout?: number, maxWait?: number, range?: Array, target?: Array, interfaceId?: Array, probe?: Array, enablePingStart?: boolean, enableBroadcast?: boolean, enableSubnetScan?: boolean, enableZeroconf?: boolean, enableNetbios?: boolean, enableResolveDns?: boolean, includeHostResults?: boolean, reportPingStart?: boolean, reportPingSuccess?: boolean, reportPingFailure?: boolean, enableTcpProbes?: boolean, rangeInterfacePolicy?: string, allowCrossInterfaceRange?: boolean, responseFormat?: string, maxConcurrency?: number, maxPingConcurrency?: number, maxTcpProbeConcurrency?: number, enableFailure?: boolean, reportTcpFailure?: boolean, interfaceBindStrict?: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarQueryParameters = new HttpParams({encoder: this.encoder}); if (pingInterval !== undefined && pingInterval !== null) { @@ -420,6 +424,7 @@ export class NetService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/networkMonitoring.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/networkMonitoring.service.ts index 1bfa710852..a121a30a00 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/networkMonitoring.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/networkMonitoring.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { MonitoringLogResponse } from '../model/monitoringLogResponse'; // @ts-ignore @@ -100,10 +102,10 @@ export class NetworkMonitoringService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public drainMonitoringLog(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public drainMonitoringLog(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public drainMonitoringLog(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public drainMonitoringLog(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public drainMonitoringLog(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public drainMonitoringLog(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public drainMonitoringLog(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public drainMonitoringLog(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -118,7 +120,8 @@ export class NetworkMonitoringService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -169,10 +172,10 @@ export class NetworkMonitoringService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public setMonitoringConfig(monitorsConfig: MonitorsConfig, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (monitorsConfig === null || monitorsConfig === undefined) { throw new Error('Required parameter monitorsConfig was null or undefined when calling setMonitoringConfig.'); } @@ -190,7 +193,8 @@ export class NetworkMonitoringService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/preflight.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/preflight.service.ts index d49f63b203..2552d0d2c5 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/preflight.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/preflight.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { PreflightOperation } from '../model/preflightOperation'; // @ts-ignore @@ -98,10 +100,10 @@ export class PreflightService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public postPreflight(preflightOperation: Array, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public postPreflight(preflightOperation: Array, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public postPreflight(preflightOperation: Array, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public postPreflight(preflightOperation: Array, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public postPreflight(preflightOperation: Array, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public postPreflight(preflightOperation: Array, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public postPreflight(preflightOperation: Array, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public postPreflight(preflightOperation: Array, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (preflightOperation === null || preflightOperation === undefined) { throw new Error('Required parameter preflightOperation was null or undefined when calling postPreflight.'); } @@ -119,7 +121,8 @@ export class PreflightService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/sessions.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/sessions.service.ts index 4c2b60cc49..db23a0568f 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/sessions.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/sessions.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { SessionInfo } from '../model/sessionInfo'; @@ -95,10 +97,10 @@ export class SessionsService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getSessions(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getSessions(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public getSessions(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public getSessions(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getSessions(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getSessions(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public getSessions(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public getSessions(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -113,7 +115,8 @@ export class SessionsService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -163,10 +166,10 @@ export class SessionsService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public terminateSession(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; - public terminateSession(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public terminateSession(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; - public terminateSession(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + public terminateSession(id: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public terminateSession(id: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public terminateSession(id: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public terminateSession(id: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (id === null || id === undefined) { throw new Error('Required parameter id was null or undefined when calling terminateSession.'); } @@ -184,6 +187,7 @@ export class SessionsService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/traffic.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/traffic.service.ts index 981f61315d..25bfa352b1 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/traffic.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/traffic.service.ts @@ -21,6 +21,8 @@ import { AckRequest } from '../model/ackRequest'; // @ts-ignore import { AckResponse } from '../model/ackResponse'; // @ts-ignore +import { ApiError } from '../model/apiError'; +// @ts-ignore import { ClaimedTrafficEvent } from '../model/claimedTrafficEvent'; // @ts-ignore @@ -100,10 +102,10 @@ export class TrafficService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public ackTrafficEvents(ackRequest: AckRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public ackTrafficEvents(ackRequest: AckRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public ackTrafficEvents(ackRequest: AckRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public ackTrafficEvents(ackRequest: AckRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public ackTrafficEvents(ackRequest: AckRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public ackTrafficEvents(ackRequest: AckRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public ackTrafficEvents(ackRequest: AckRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public ackTrafficEvents(ackRequest: AckRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (ackRequest === null || ackRequest === undefined) { throw new Error('Required parameter ackRequest was null or undefined when calling ackTrafficEvents.'); } @@ -121,7 +123,8 @@ export class TrafficService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -182,10 +185,10 @@ export class TrafficService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public claimTrafficEvents(leaseMs: number, max: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public claimTrafficEvents(leaseMs: number, max: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public claimTrafficEvents(leaseMs: number, max: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; - public claimTrafficEvents(leaseMs: number, max: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public claimTrafficEvents(leaseMs: number, max: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public claimTrafficEvents(leaseMs: number, max: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public claimTrafficEvents(leaseMs: number, max: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>>; + public claimTrafficEvents(leaseMs: number, max: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (leaseMs === null || leaseMs === undefined) { throw new Error('Required parameter leaseMs was null or undefined when calling claimTrafficEvents.'); } @@ -216,7 +219,8 @@ export class TrafficService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/api/update.service.ts b/devolutions-gateway/openapi/ts-angular-client/api/update.service.ts index 446252e569..4f3b0b0a96 100644 --- a/devolutions-gateway/openapi/ts-angular-client/api/update.service.ts +++ b/devolutions-gateway/openapi/ts-angular-client/api/update.service.ts @@ -16,6 +16,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; +// @ts-ignore +import { ApiError } from '../model/apiError'; // @ts-ignore import { GetUpdateProductsResponse } from '../model/getUpdateProductsResponse'; // @ts-ignore @@ -102,10 +104,10 @@ export class UpdateService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getUpdateProducts(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getUpdateProducts(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getUpdateProducts(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getUpdateProducts(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getUpdateProducts(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getUpdateProducts(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getUpdateProducts(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getUpdateProducts(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -120,7 +122,8 @@ export class UpdateService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -170,10 +173,10 @@ export class UpdateService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public getUpdateSchedule(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public getUpdateSchedule(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getUpdateSchedule(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public getUpdateSchedule(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public getUpdateSchedule(observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public getUpdateSchedule(observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getUpdateSchedule(observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getUpdateSchedule(observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarHeaders = this.defaultHeaders; @@ -188,7 +191,8 @@ export class UpdateService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -239,10 +243,10 @@ export class UpdateService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public setUpdateSchedule(setUpdateScheduleRequest: SetUpdateScheduleRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { if (setUpdateScheduleRequest === null || setUpdateScheduleRequest === undefined) { throw new Error('Required parameter setUpdateScheduleRequest was null or undefined when calling setUpdateSchedule.'); } @@ -260,7 +264,8 @@ export class UpdateService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } @@ -322,10 +327,10 @@ export class UpdateService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; - public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; - public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable; + public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable>; + public triggerUpdate(version?: string, updateRequestSchema?: UpdateRequestSchema, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json' | 'application/problem+json', context?: HttpContext, transferCache?: boolean}): Observable { let localVarQueryParameters = new HttpParams({encoder: this.encoder}); if (version !== undefined && version !== null) { @@ -346,7 +351,8 @@ export class UpdateService { if (localVarHttpHeaderAcceptSelected === undefined) { // to determine the Accept header const httpHeaderAccepts: string[] = [ - 'application/json' + 'application/json', + 'application/problem+json' ]; localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); } diff --git a/devolutions-gateway/openapi/ts-angular-client/model/apiError.ts b/devolutions-gateway/openapi/ts-angular-client/model/apiError.ts new file mode 100644 index 0000000000..d5c295123b --- /dev/null +++ b/devolutions-gateway/openapi/ts-angular-client/model/apiError.ts @@ -0,0 +1,33 @@ +/** + * devolutions-gateway + * + * Contact: infos@devolutions.net + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Body of an API error response, as defined by RFC 9457 (problem details for HTTP APIs). The Gateway logs the details of the error, along with its code. + */ +export interface ApiError { + /** + * Machine-readable error code; the `ErrorCode` schema lists the known values. New codes can be added: treat unknown values as generic failures. + */ + error: string; + /** + * HTTP status code. + */ + status: number; + /** + * Reason phrase of the HTTP status. + */ + title: string; + /** + * Problem type, always `about:blank`: the HTTP status and the error code describe the problem. + */ + type: string; +} + diff --git a/devolutions-gateway/openapi/ts-angular-client/model/errorCode.ts b/devolutions-gateway/openapi/ts-angular-client/model/errorCode.ts new file mode 100644 index 0000000000..d7813470a5 --- /dev/null +++ b/devolutions-gateway/openapi/ts-angular-client/model/errorCode.ts @@ -0,0 +1,43 @@ +/** + * devolutions-gateway + * + * Contact: infos@devolutions.net + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Machine-readable reason for a rejected request. New codes can be added, but existing codes are never renamed or removed. Clients must treat unknown codes as generic failures. - `bad_request`: The request is invalid. - `unauthorized`: The request is not authenticated. - `forbidden`: The request is not allowed. - `not_found`: The resource doesn\'t exist. - `request_timeout`: The request took too long to complete. - `conflict`: The request conflicts with the current state of the resource. - `payload_too_large`: The request is too large. - `internal_error`: The Gateway failed to process the request. - `bad_gateway`: The Gateway received an invalid response from an upstream server. - `service_unavailable`: The service is temporarily unavailable. - `gateway_timeout`: An upstream server didn\'t respond in time. - `insufficient_storage`: The Gateway is out of storage space. - `missing_token`: No token was found in the `Authorization` header or in the `token` query parameter. - `malformed_token`: The token can\'t be decoded or decrypted, or its claims don\'t match the schema expected for its type. - `bad_signature`: The token signature doesn\'t match the provisioner key of the Gateway. - `unknown_subkey`: The token refers to a key ID (`kid`) that doesn\'t match the sub provisioner key of the Gateway. - `expired`: The token has expired (`exp` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `not_yet_valid`: The token is not valid yet (`nbf` claim); the clocks of the token issuer and of the Gateway may be out of sync. - `gateway_id_mismatch`: The token is restricted to another Gateway ID (`jet_gw_id` claim). - `revoked`: The token contains a value revoked by the JSON Revocation List (JRL) of the Gateway. - `unexpected_replay`: The token was already used, and can\'t be used again in this context. - `invalid_token`: The token is rejected for a reason not covered by another code. - `wrong_token_type`: The type of the token is not accepted by this route. - `insufficient_scope`: The scope of the token doesn\'t grant access to this route. + */ +export type ErrorCode = 'bad_request' | 'unauthorized' | 'forbidden' | 'not_found' | 'request_timeout' | 'conflict' | 'payload_too_large' | 'internal_error' | 'bad_gateway' | 'service_unavailable' | 'gateway_timeout' | 'insufficient_storage' | 'missing_token' | 'malformed_token' | 'bad_signature' | 'unknown_subkey' | 'expired' | 'not_yet_valid' | 'gateway_id_mismatch' | 'revoked' | 'unexpected_replay' | 'invalid_token' | 'wrong_token_type' | 'insufficient_scope'; + +export const ErrorCode = { + BadRequest: 'bad_request' as ErrorCode, + Unauthorized: 'unauthorized' as ErrorCode, + Forbidden: 'forbidden' as ErrorCode, + NotFound: 'not_found' as ErrorCode, + RequestTimeout: 'request_timeout' as ErrorCode, + Conflict: 'conflict' as ErrorCode, + PayloadTooLarge: 'payload_too_large' as ErrorCode, + InternalError: 'internal_error' as ErrorCode, + BadGateway: 'bad_gateway' as ErrorCode, + ServiceUnavailable: 'service_unavailable' as ErrorCode, + GatewayTimeout: 'gateway_timeout' as ErrorCode, + InsufficientStorage: 'insufficient_storage' as ErrorCode, + MissingToken: 'missing_token' as ErrorCode, + MalformedToken: 'malformed_token' as ErrorCode, + BadSignature: 'bad_signature' as ErrorCode, + UnknownSubkey: 'unknown_subkey' as ErrorCode, + Expired: 'expired' as ErrorCode, + NotYetValid: 'not_yet_valid' as ErrorCode, + GatewayIdMismatch: 'gateway_id_mismatch' as ErrorCode, + Revoked: 'revoked' as ErrorCode, + UnexpectedReplay: 'unexpected_replay' as ErrorCode, + InvalidToken: 'invalid_token' as ErrorCode, + WrongTokenType: 'wrong_token_type' as ErrorCode, + InsufficientScope: 'insufficient_scope' as ErrorCode +}; + diff --git a/devolutions-gateway/openapi/ts-angular-client/model/models.ts b/devolutions-gateway/openapi/ts-angular-client/model/models.ts index 7d7eb4f0fe..d81cf555db 100644 --- a/devolutions-gateway/openapi/ts-angular-client/model/models.ts +++ b/devolutions-gateway/openapi/ts-angular-client/model/models.ts @@ -5,6 +5,7 @@ export * from './addressFamily'; export * from './agentDomainAdvertisement'; export * from './agentInfo'; export * from './agentStatus'; +export * from './apiError'; export * from './appCredential'; export * from './appCredentialKind'; export * from './appTokenContentType'; @@ -18,6 +19,7 @@ export * from './dataEncoding'; export * from './deleteManyResult'; export * from './enrollRequest'; export * from './enrollResponse'; +export * from './errorCode'; export * from './eventOutcomeResponse'; export * from './getUpdateProductsResponse'; export * from './getUpdateScheduleResponse'; From e4d9802e69179b05ddc494d01c77cd3366d8ef03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Thu, 15 Oct 2026 17:12:59 +0900 Subject: [PATCH 3/5] test(dgw): expect the problem details body on rejected web app logins Issue: DGW-467 Co-Authored-By: Claude Opus 5.5 --- devolutions-gateway/tests/webapp.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/devolutions-gateway/tests/webapp.rs b/devolutions-gateway/tests/webapp.rs index 8e090e9d10..2be62d595b 100644 --- a/devolutions-gateway/tests/webapp.rs +++ b/devolutions-gateway/tests/webapp.rs @@ -258,7 +258,7 @@ async fn sign_app_token_bad_password() -> anyhow::Result<()> { assert_eq!(response.status(), StatusCode::UNAUTHORIZED); let body = response.into_body().collect().await?.to_bytes(); - assert!(body.is_empty()); + assert_unauthorized_body(&body)?; Ok(()) } @@ -296,7 +296,19 @@ async fn sign_app_token_username_mismatch() -> anyhow::Result<()> { assert_eq!(response.status(), StatusCode::UNAUTHORIZED); let body = response.into_body().collect().await?.to_bytes(); - assert!(body.is_empty()); + assert_unauthorized_body(&body)?; + + Ok(()) +} + +/// Asserts that a rejected login carries the generic code only, whatever the reason. +fn assert_unauthorized_body(body: &[u8]) -> anyhow::Result<()> { + let body: serde_json::Value = serde_json::from_slice(body)?; + + assert_eq!( + body, + json!({ "type": "about:blank", "title": "Unauthorized", "status": 401, "error": "unauthorized" }) + ); Ok(()) } From 5c66ba6da9ac6b1a0489a473cd72c7352215112e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Fri, 23 Oct 2026 03:18:34 +0900 Subject: [PATCH 4/5] refactor(dgw): cover the recording search route and move unit tests in-crate Tag the 403 of the new recording search scope with `insufficient_scope`, and declare the problem details body on the error responses of `/jet/jrec/search`. Move the `TokenError` mapping and code list tests next to the code, as `testsuite/CLAUDE.md` asks for tests of Gateway internals. Issue: DGW-467 Co-Authored-By: Claude Opus 5.5 --- devolutions-gateway/src/api/jrec/search.rs | 6 +- devolutions-gateway/src/error_code.rs | 67 ++++++++++ devolutions-gateway/src/extract.rs | 4 +- devolutions-gateway/src/token.rs | 87 +++++++++++++ testsuite/tests/error_codes.rs | 138 --------------------- testsuite/tests/main.rs | 1 - 6 files changed, 160 insertions(+), 143 deletions(-) delete mode 100644 testsuite/tests/error_codes.rs diff --git a/devolutions-gateway/src/api/jrec/search.rs b/devolutions-gateway/src/api/jrec/search.rs index 9147236741..cdf92d1c84 100644 --- a/devolutions-gateway/src/api/jrec/search.rs +++ b/devolutions-gateway/src/api/jrec/search.rs @@ -164,9 +164,9 @@ pub(crate) struct RecordingSearchHit { request_body(content = RecordingSearchRequest, description = "JSON-encoded search request", content_type = "application/json"), responses( (status = 200, description = "Matching log lines", body = RecordingSearchResponse), - (status = 400, description = "Bad request"), - (status = 401, description = "Invalid or missing authorization token"), - (status = 403, description = "Insufficient permissions"), + (status = 400, description = "Bad request", body = ApiError, content_type = "application/problem+json"), + (status = 401, description = "Invalid or missing authorization token", body = ApiError, content_type = "application/problem+json"), + (status = 403, description = "Insufficient permissions", body = ApiError, content_type = "application/problem+json"), ), security(("scope_token" = ["gateway.recordings.search"])), ))] diff --git a/devolutions-gateway/src/error_code.rs b/devolutions-gateway/src/error_code.rs index 066a33f1fc..6ca101b09d 100644 --- a/devolutions-gateway/src/error_code.rs +++ b/devolutions-gateway/src/error_code.rs @@ -145,3 +145,70 @@ impl<'s> utoipa::ToSchema<'s> for ErrorCode { ("ErrorCode", schema.into()) } } + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use expect_test::expect; + + use super::*; + + /// Codes are public API: existing codes are never renamed or removed, and new codes are appended. + #[test] + fn codes_are_stable() { + let codes = ErrorCode::ALL + .iter() + .map(|code| code.as_str()) + .collect::>() + .join("\n"); + + expect![[r#" + bad_request + unauthorized + forbidden + not_found + request_timeout + conflict + payload_too_large + internal_error + bad_gateway + service_unavailable + gateway_timeout + insufficient_storage + missing_token + malformed_token + bad_signature + unknown_subkey + expired + not_yet_valid + gateway_id_mismatch + revoked + unexpected_replay + invalid_token + wrong_token_type + insufficient_scope"#]] + .assert_eq(&codes); + } + + #[test] + fn codes_are_unique_snake_case_and_documented() { + let mut seen = HashSet::new(); + + for code in ErrorCode::ALL { + let code_str = code.as_str(); + + assert!(seen.insert(code_str), "duplicated code: {code_str}"); + assert!( + code_str.starts_with(|c: char| c.is_ascii_lowercase()) + && code_str.chars().all(|c| c.is_ascii_lowercase() || c == '_'), + "not snake_case: {code_str}" + ); + assert!(!code.description().is_empty(), "undocumented code: {code_str}"); + assert_eq!( + serde_json::to_value(code).expect("serialize the code"), + serde_json::Value::from(code_str) + ); + } + } +} diff --git a/devolutions-gateway/src/extract.rs b/devolutions-gateway/src/extract.rs index 2e7e1f4888..0a6d3dbb1a 100644 --- a/devolutions-gateway/src/extract.rs +++ b/devolutions-gateway/src/extract.rs @@ -365,7 +365,9 @@ where match ScopeToken::from_request_parts(parts, state).await?.0.scope { AccessScope::Wildcard => Ok(Self), AccessScope::RecordingsSearch => Ok(Self), - _ => Err(HttpError::forbidden().msg("invalid scope for route")), + _ => Err(HttpError::forbidden() + .with_error_code(ErrorCode::InsufficientScope) + .msg("invalid scope for route")), } } } diff --git a/devolutions-gateway/src/token.rs b/devolutions-gateway/src/token.rs index 03e195c7fc..b98a6ec5dd 100644 --- a/devolutions-gateway/src/token.rs +++ b/devolutions-gateway/src/token.rs @@ -2049,4 +2049,91 @@ mod tests { assert_eq!(recording_file_type.content_type(), expected_content_type); } } + + mod error_code { + use picky::jose::jwe::JweError; + use picky::jose::jws::JwsError; + use picky::jose::jwt::{JwtDate, JwtError}; + use rstest::rstest; + + use super::super::*; + + fn invalid_utf8() -> std::str::Utf8Error { + std::str::from_utf8(std::hint::black_box(&[0xC0])).expect_err("invalid UTF-8") + } + + fn json_error() -> serde_json::Error { + serde_json::from_str::("not JSON").expect_err("invalid JSON") + } + + fn jws_error() -> JwsError { + JwsError::InvalidEncoding { input: String::new() } + } + + #[rstest] + // Raised before the signature is verified. + #[case::missing_delegation_key(TokenError::MissingDelegationKey, ErrorCode::MalformedToken)] + #[case::jwe(TokenError::Jwe { source: JweError::AesGcm }, ErrorCode::MalformedToken)] + #[case::jwe_payload(TokenError::JwePayload { source: invalid_utf8() }, ErrorCode::MalformedToken)] + #[case::jws(TokenError::Jws { source: jws_error() }, ErrorCode::MalformedToken)] + #[case::signature_verification( + TokenError::SignatureVerification { source: jws_error(), key: "main provisioner key" }, + ErrorCode::BadSignature + )] + #[case::unknown_subkey(TokenError::UnknownSubkey { provided_kid: String::new() }, ErrorCode::UnknownSubkey)] + // Raised after the signature is verified. + #[case::bad_content_type( + TokenError::BadContentType { source: "UNKNOWN".parse::().expect_err("unknown content type") }, + ErrorCode::MalformedToken + )] + #[case::expired( + TokenError::Jwt { source: JwtError::Expired { not_after: 0, now: JwtDate::new(1) } }, + ErrorCode::Expired + )] + #[case::not_yet_valid( + TokenError::Jwt { source: JwtError::NotYetValid { not_before: 1, now: JwtDate::new(0) } }, + ErrorCode::NotYetValid + )] + #[case::required_claim_missing( + TokenError::Jwt { source: JwtError::RequiredClaimMissing { claim: "exp" } }, + ErrorCode::MalformedToken + )] + #[case::invalid_registered_claim_type( + TokenError::Jwt { source: JwtError::InvalidRegisteredClaimType { claim: "exp" } }, + ErrorCode::MalformedToken + )] + #[case::claims_json(TokenError::Jwt { source: JwtError::Json { source: json_error() } }, ErrorCode::MalformedToken)] + #[case::invalid_validator( + TokenError::Jwt { source: JwtError::InvalidValidator { description: "" } }, + ErrorCode::InvalidToken + )] + #[case::content_type_not_allowed_for_subkey( + TokenError::ContentTypeNotAllowedForSubkey { content_type: ContentType::Scope }, + ErrorCode::InvalidToken + )] + #[case::malformed_claim( + TokenError::MalformedClaim { name: "jet_gw_id", source: anyhow::anyhow!("invalid UUID") }, + ErrorCode::MalformedToken + )] + #[case::gateway_id_scope_mismatch(TokenError::GatewayIdScopeMismatch, ErrorCode::GatewayIdMismatch)] + #[case::revoked(TokenError::Revoked, ErrorCode::Revoked)] + #[case::invalid_claim_scheme( + TokenError::InvalidClaimScheme { content_type: ContentType::Scope, source: json_error() }, + ErrorCode::MalformedToken + )] + #[case::plaintext_secrets(TokenError::PlaintextSecrets, ErrorCode::InvalidToken)] + #[case::unexpected_replay( + TokenError::UnexpectedReplay { reason: "different source IP" }, + ErrorCode::UnexpectedReplay + )] + #[case::old_jrl(TokenError::OldJrl, ErrorCode::InvalidToken)] + #[case::subkey_signed_and_too_long_lifetime( + TokenError::SubkeySignedAndTooLongLifetime, + ErrorCode::InvalidToken + )] + #[case::too_long_lifetime(TokenError::TooLongLifetime, ErrorCode::InvalidToken)] + fn token_error_code(#[case] error: TokenError, #[case] expected_code: ErrorCode) { + assert_eq!(error.code(), expected_code); + } + } } diff --git a/testsuite/tests/error_codes.rs b/testsuite/tests/error_codes.rs deleted file mode 100644 index 2b221c6b6f..0000000000 --- a/testsuite/tests/error_codes.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! Machine-readable error codes reported to clients. - -use std::collections::HashSet; - -use devolutions_gateway::error_code::ErrorCode; -use devolutions_gateway::token::{ContentType, TokenError}; -use expect_test::expect; -use picky::jose::jwe::JweError; -use picky::jose::jws::JwsError; -use picky::jose::jwt::{JwtDate, JwtError}; -use rstest::rstest; - -fn invalid_utf8() -> std::str::Utf8Error { - std::str::from_utf8(std::hint::black_box(&[0xC0])).unwrap_err() -} - -fn json_error() -> serde_json::Error { - serde_json::from_str::("not JSON").unwrap_err() -} - -fn jws_error() -> JwsError { - JwsError::InvalidEncoding { input: String::new() } -} - -#[rstest] -// Raised before the signature is verified. -#[case::missing_delegation_key(TokenError::MissingDelegationKey, "malformed_token")] -#[case::jwe(TokenError::Jwe { source: JweError::AesGcm }, "malformed_token")] -#[case::jwe_payload(TokenError::JwePayload { source: invalid_utf8() }, "malformed_token")] -#[case::jws(TokenError::Jws { source: jws_error() }, "malformed_token")] -#[case::signature_verification( - TokenError::SignatureVerification { source: jws_error(), key: "main provisioner key" }, - "bad_signature" -)] -#[case::unknown_subkey(TokenError::UnknownSubkey { provided_kid: String::new() }, "unknown_subkey")] -// Raised after the signature is verified. -#[case::bad_content_type( - TokenError::BadContentType { source: "UNKNOWN".parse::().unwrap_err() }, - "malformed_token" -)] -#[case::expired( - TokenError::Jwt { source: JwtError::Expired { not_after: 0, now: JwtDate::new(1) } }, - "expired" -)] -#[case::not_yet_valid( - TokenError::Jwt { source: JwtError::NotYetValid { not_before: 1, now: JwtDate::new(0) } }, - "not_yet_valid" -)] -#[case::required_claim_missing( - TokenError::Jwt { source: JwtError::RequiredClaimMissing { claim: "exp" } }, - "malformed_token" -)] -#[case::invalid_registered_claim_type( - TokenError::Jwt { source: JwtError::InvalidRegisteredClaimType { claim: "exp" } }, - "malformed_token" -)] -#[case::claims_json(TokenError::Jwt { source: JwtError::Json { source: json_error() } }, "malformed_token")] -#[case::invalid_validator( - TokenError::Jwt { source: JwtError::InvalidValidator { description: "" } }, - "invalid_token" -)] -#[case::content_type_not_allowed_for_subkey( - TokenError::ContentTypeNotAllowedForSubkey { content_type: ContentType::Scope }, - "invalid_token" -)] -#[case::malformed_claim( - TokenError::MalformedClaim { name: "jet_gw_id", source: anyhow::anyhow!("invalid UUID") }, - "malformed_token" -)] -#[case::gateway_id_scope_mismatch(TokenError::GatewayIdScopeMismatch, "gateway_id_mismatch")] -#[case::revoked(TokenError::Revoked, "revoked")] -#[case::invalid_claim_scheme( - TokenError::InvalidClaimScheme { content_type: ContentType::Scope, source: json_error() }, - "malformed_token" -)] -#[case::plaintext_secrets(TokenError::PlaintextSecrets, "invalid_token")] -#[case::unexpected_replay(TokenError::UnexpectedReplay { reason: "different source IP" }, "unexpected_replay")] -#[case::old_jrl(TokenError::OldJrl, "invalid_token")] -#[case::subkey_signed_and_too_long_lifetime(TokenError::SubkeySignedAndTooLongLifetime, "invalid_token")] -#[case::too_long_lifetime(TokenError::TooLongLifetime, "invalid_token")] -fn token_error_code(#[case] error: TokenError, #[case] expected_code: &str) { - assert_eq!(error.code().as_str(), expected_code); -} - -/// Codes are public API: existing codes are never renamed or removed, and new codes are appended. -#[test] -fn codes_are_stable() { - let codes = ErrorCode::ALL - .iter() - .map(|code| code.as_str()) - .collect::>() - .join("\n"); - - expect![[r#" - bad_request - unauthorized - forbidden - not_found - request_timeout - conflict - payload_too_large - internal_error - bad_gateway - service_unavailable - gateway_timeout - insufficient_storage - missing_token - malformed_token - bad_signature - unknown_subkey - expired - not_yet_valid - gateway_id_mismatch - revoked - unexpected_replay - invalid_token - wrong_token_type - insufficient_scope"#]] - .assert_eq(&codes); -} - -#[test] -fn codes_are_unique_snake_case_and_documented() { - let mut seen = HashSet::new(); - - for code in ErrorCode::ALL { - let code_str = code.as_str(); - - assert!(seen.insert(code_str), "duplicated code: {code_str}"); - assert!( - code_str.starts_with(|c: char| c.is_ascii_lowercase()) - && code_str.chars().all(|c| c.is_ascii_lowercase() || c == '_'), - "not snake_case: {code_str}" - ); - assert!(!code.description().is_empty(), "undocumented code: {code_str}"); - assert_eq!(serde_json::to_value(code).unwrap(), code_str); - } -} diff --git a/testsuite/tests/main.rs b/testsuite/tests/main.rs index 7707aa3c93..4b5861aa83 100644 --- a/testsuite/tests/main.rs +++ b/testsuite/tests/main.rs @@ -4,7 +4,6 @@ mod agent_tunnel; mod cli; -mod error_codes; mod mcp_proxy; mod network_scanner; #[cfg(windows)] From f1ed3be07360f9eb217ed0d59c2e10a41dbd6bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20CORTIER?= Date: Fri, 23 Oct 2026 04:38:41 +0900 Subject: [PATCH 5/5] test(dgw): mark the oracles of the error code tests Cite the documented error format and codes as the source of each new test's expected values, as the oracle marker check requires. - Document that a SCOPE token is valid for a single request, which the `unexpected_replay` test relies on. - Check `invalid_token` with an outdated revocation list, a documented rule, instead of the undocumented BRIDGE token lifetime limit. - Drop the uniqueness and format test of the code list: no source decides it, and the code list snapshot already pins every code. Issue: DGW-467 Co-Authored-By: Claude Opus 5.5 --- devolutions-gateway/src/error_code.rs | 24 +--------- devolutions-gateway/src/token.rs | 1 + docs/API-spec.md | 4 ++ testsuite/tests/cli/dgw/api_errors.rs | 64 +++++++++++++++++++-------- 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/devolutions-gateway/src/error_code.rs b/devolutions-gateway/src/error_code.rs index 6ca101b09d..efa2789196 100644 --- a/devolutions-gateway/src/error_code.rs +++ b/devolutions-gateway/src/error_code.rs @@ -148,13 +148,12 @@ impl<'s> utoipa::ToSchema<'s> for ErrorCode { #[cfg(test)] mod tests { - use std::collections::HashSet; - use expect_test::expect; use super::*; /// Codes are public API: existing codes are never renamed or removed, and new codes are appended. + // oracle: spec docs/API-spec.md §errors #[test] fn codes_are_stable() { let codes = ErrorCode::ALL @@ -190,25 +189,4 @@ mod tests { insufficient_scope"#]] .assert_eq(&codes); } - - #[test] - fn codes_are_unique_snake_case_and_documented() { - let mut seen = HashSet::new(); - - for code in ErrorCode::ALL { - let code_str = code.as_str(); - - assert!(seen.insert(code_str), "duplicated code: {code_str}"); - assert!( - code_str.starts_with(|c: char| c.is_ascii_lowercase()) - && code_str.chars().all(|c| c.is_ascii_lowercase() || c == '_'), - "not snake_case: {code_str}" - ); - assert!(!code.description().is_empty(), "undocumented code: {code_str}"); - assert_eq!( - serde_json::to_value(code).expect("serialize the code"), - serde_json::Value::from(code_str) - ); - } - } } diff --git a/devolutions-gateway/src/token.rs b/devolutions-gateway/src/token.rs index b98a6ec5dd..32b56aedbd 100644 --- a/devolutions-gateway/src/token.rs +++ b/devolutions-gateway/src/token.rs @@ -2070,6 +2070,7 @@ mod tests { JwsError::InvalidEncoding { input: String::new() } } + // oracle: spec docs/API-spec.md §token-errors #[rstest] // Raised before the signature is verified. #[case::missing_delegation_key(TokenError::MissingDelegationKey, ErrorCode::MalformedToken)] diff --git a/docs/API-spec.md b/docs/API-spec.md index b907f57913..d0a864574b 100644 --- a/docs/API-spec.md +++ b/docs/API-spec.md @@ -212,6 +212,8 @@ Once jetsocat is successfully connected to the Devolutions Gateway, you can conf ### SCOPE +A SCOPE token is valid for a single request: the Gateway rejects a token it has already accepted with the `unexpected_replay` error code. + #### Claims ```json5 @@ -470,6 +472,7 @@ i.e.: } ``` + ## Errors Error responses carry a machine-readable body, following [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (problem details for HTTP APIs), with the `application/problem+json` content type: @@ -502,6 +505,7 @@ When no specific code applies, the error code is derived from the HTTP status: Some errors don't carry this body yet, such as the errors raised while parsing a request body, and the `400` responses of `/jet/net/scan`, which have their own format. + ### Token and authorization errors | Code | Status | Meaning | diff --git a/testsuite/tests/cli/dgw/api_errors.rs b/testsuite/tests/cli/dgw/api_errors.rs index c33332319d..c4bd50b3e6 100644 --- a/testsuite/tests/cli/dgw/api_errors.rs +++ b/testsuite/tests/cli/dgw/api_errors.rs @@ -149,23 +149,12 @@ fn scope_token(claims: &Value) -> String { sign_token(&provisioner_key(), "SCOPE", None, claims) } -/// A BRIDGE token whose lifetime exceeds the 12-hour maximum. -fn too_long_bridge_token() -> String { - let now = now(); - let claims = json!({ - "target_host": "http://localhost", - "jet_aid": Uuid::new_v4(), - "nbf": now - 60, - "exp": now + 13 * 3600, - }); - sign_token(&provisioner_key(), "BRIDGE", None, &claims) -} - -fn jrl_token(jrl: Value) -> String { - let claims = json!({ "jti": Uuid::new_v4(), "iat": now(), "jrl": jrl }); +fn jrl_token(iat: i64, jrl: Value) -> String { + let claims = json!({ "jti": Uuid::new_v4(), "iat": iat, "jrl": jrl }); sign_token(&provisioner_key(), "JRL", None, &claims) } +// oracle: spec docs/API-spec.md §errors #[tokio::test] async fn missing_token() { let gateway = Gateway::start().await; @@ -178,6 +167,7 @@ async fn missing_token() { gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors #[rstest] #[case::malformed_token("not-a-token".to_owned(), "malformed_token")] #[case::malformed_claims(scope_token(&without_claim(heartbeat_claims(), "scope")), "malformed_token")] @@ -186,7 +176,6 @@ async fn missing_token() { #[case::expired(scope_token(&expired_claims()), "expired")] #[case::not_yet_valid(scope_token(¬_yet_valid_claims()), "not_yet_valid")] #[case::gateway_id_mismatch(scope_token(&other_gateway_claims()), "gateway_id_mismatch")] -#[case::invalid_token(too_long_bridge_token(), "invalid_token")] #[tokio::test] async fn rejected_token(#[case] token: String, #[case] expected_error: &str) { let gateway = Gateway::start().await; @@ -200,6 +189,7 @@ async fn rejected_token(#[case] token: String, #[case] expected_error: &str) { /// The signature is verified before any claim: without a token signed by a trusted key, /// a caller learns nothing about the claims the Gateway expects. +// oracle: spec docs/API-spec.md §errors #[rstest] #[case::expired(expired_claims())] #[case::not_yet_valid(not_yet_valid_claims())] @@ -217,6 +207,8 @@ async fn claims_are_not_disclosed_without_a_valid_signature(#[case] claims: Valu gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §SCOPE #[tokio::test] async fn unexpected_replay() { let gateway = Gateway::start().await; @@ -226,13 +218,15 @@ async fn unexpected_replay() { let first = gateway.get_heartbeat(Some(&token)).await; assert_eq!(first.status, 200, "{first:?}"); - // Scope tokens can't be reused. + // A SCOPE token is valid for a single request. let second = gateway.get_heartbeat(Some(&token)).await; second.assert_unauthorized("unexpected_replay"); gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §JRL (JWT Revocation List) #[tokio::test] async fn revoked() { let gateway = Gateway::start().await; @@ -243,7 +237,7 @@ async fn revoked() { .request( reqwest::Method::POST, "/jet/jrl", - Some(&jrl_token(json!({ "jti": [claims["jti"]] }))), + Some(&jrl_token(now(), json!({ "jti": [claims["jti"]] }))), ) .await; assert_eq!(jrl_update.status, 200, "{jrl_update:?}"); @@ -255,18 +249,49 @@ async fn revoked() { gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §JRL (JWT Revocation List) +#[tokio::test] +async fn outdated_revocation_list() { + let gateway = Gateway::start().await; + + let now = now(); + + let jrl_update = gateway + .request(reqwest::Method::POST, "/jet/jrl", Some(&jrl_token(now, json!({})))) + .await; + assert_eq!(jrl_update.status, 200, "{jrl_update:?}"); + + // The Gateway only accepts a revocation list newer than its current one. + let response = gateway + .request( + reqwest::Method::POST, + "/jet/jrl", + Some(&jrl_token(now - 3600, json!({}))), + ) + .await; + + response.assert_unauthorized("invalid_token"); + + gateway.stop().await; +} + +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §About scopes #[tokio::test] async fn wrong_token_type() { let gateway = Gateway::start().await; // A valid token, but the route expects a SCOPE token. - let response = gateway.get_heartbeat(Some(&jrl_token(json!({})))).await; + let response = gateway.get_heartbeat(Some(&jrl_token(now(), json!({})))).await; response.assert_problem(403, "Forbidden", "wrong_token_type", None); gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §About scopes #[tokio::test] async fn insufficient_scope() { let gateway = Gateway::start().await; @@ -284,6 +309,8 @@ async fn insufficient_scope() { gateway.stop().await; } +// oracle: spec docs/API-spec.md §errors +// oracle: spec docs/API-spec.md §OpenAPI #[tokio::test] async fn generic_code_derived_from_status() { let gateway = Gateway::start().await; @@ -300,6 +327,7 @@ async fn generic_code_derived_from_status() { } /// The KDC proxy reads its token from the path, and validates it outside of the authentication middleware. +// oracle: spec docs/API-spec.md §errors #[tokio::test] async fn rejected_kdc_token() { let gateway = Gateway::start().await;