Skip to main content

hanzo_client/apis/
kms_api.rs

1/*
2 * Hanzo Cloud API
3 *
4 * The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
5 *
6 * The version of the OpenAPI document: v1
7 * 
8 * Generated by: https://openapi-generator.tech
9 */
10
11
12use reqwest;
13use serde::{Deserialize, Serialize, de::Error as _};
14use crate::{apis::ResponseContent, models};
15use super::{Error, configuration, ContentType};
16
17
18/// struct for typed errors of method [`get_kms_config`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum GetKmsConfigError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`get_kms_health`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum GetKmsHealthError {
29    Status503(models::KmsHealth),
30    UnknownValue(serde_json::Value),
31}
32
33/// struct for typed errors of method [`get_kms_secrets`]
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(untagged)]
36pub enum GetKmsSecretsError {
37    UnknownValue(serde_json::Value),
38}
39
40/// struct for typed errors of method [`post_kms_auth_login`]
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum PostKmsAuthLoginError {
44    UnknownValue(serde_json::Value),
45}
46
47/// struct for typed errors of method [`post_kms_secrets`]
48#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(untagged)]
50pub enum PostKmsSecretsError {
51    UnknownValue(serde_json::Value),
52}
53
54
55/// Returns the runtime configuration for the KMS console.  What the console needs before anyone has signed in: the brand, the OIDC issuer it authenticates against, the API base for this subsystem and the path of the login exchange.  Public on purpose, and it holds nothing sensitive — it is deliberately kept under this subsystem's own namespace rather than under an admin prefix, so a gateway that admin-gates the admin routes cannot break the console's legitimate pre-login fetch.
56pub async fn get_kms_config(configuration: &configuration::Configuration, ) -> Result<models::KmsConfig, Error<GetKmsConfigError>> {
57
58    let uri_str = format!("{}/v1/kms/config", configuration.base_path);
59    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
60
61    if let Some(ref user_agent) = configuration.user_agent {
62        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
63    }
64    if let Some(ref token) = configuration.bearer_access_token {
65        req_builder = req_builder.bearer_auth(token.to_owned());
66    };
67
68    let req = req_builder.build()?;
69    let resp = configuration.client.execute(req).await?;
70
71    let status = resp.status();
72    let content_type = resp
73        .headers()
74        .get("content-type")
75        .and_then(|v| v.to_str().ok())
76        .unwrap_or("application/octet-stream");
77    let content_type = super::ContentType::from(content_type);
78
79    if !status.is_client_error() && !status.is_server_error() {
80        let content = resp.text().await?;
81        match content_type {
82            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
83            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::KmsConfig`"))),
84            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::KmsConfig`")))),
85        }
86    } else {
87        let content = resp.text().await?;
88        let entity: Option<GetKmsConfigError> = serde_json::from_str(&content).ok();
89        Err(Error::ResponseError(ResponseContent { status, content, entity }))
90    }
91}
92
93/// Reports whether this broker can actually serve secrets.  A real readiness probe, not a liveness stub: 200 only when the store is open AND a master key is configured, with `signing` reporting whether signing keys are set up too. Anything less answers 503 with `ready:false` and the reason — no in-process store, or no master key — which are exactly the two states in which the secret operations refuse.  Not token-gated, because the platform must be able to probe it without a credential. It reports the broker's configuration state only; no secret, no key material and no tenant name appears in it.
94pub async fn get_kms_health(configuration: &configuration::Configuration, ) -> Result<models::KmsHealth, Error<GetKmsHealthError>> {
95
96    let uri_str = format!("{}/v1/kms/health", configuration.base_path);
97    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
98
99    if let Some(ref user_agent) = configuration.user_agent {
100        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
101    }
102    if let Some(ref token) = configuration.bearer_access_token {
103        req_builder = req_builder.bearer_auth(token.to_owned());
104    };
105
106    let req = req_builder.build()?;
107    let resp = configuration.client.execute(req).await?;
108
109    let status = resp.status();
110    let content_type = resp
111        .headers()
112        .get("content-type")
113        .and_then(|v| v.to_str().ok())
114        .unwrap_or("application/octet-stream");
115    let content_type = super::ContentType::from(content_type);
116
117    if !status.is_client_error() && !status.is_server_error() {
118        let content = resp.text().await?;
119        match content_type {
120            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
121            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::KmsHealth`"))),
122            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::KmsHealth`")))),
123        }
124    } else {
125        let content = resp.text().await?;
126        let entity: Option<GetKmsHealthError> = serde_json::from_str(&content).ok();
127        Err(Error::ResponseError(ResponseContent { status, content, entity }))
128    }
129}
130
131/// Lists the secrets your org holds, without their values.  Returns the METADATA of the caller's own secrets: each one's name, path, environment and sealing scheme. No value and no ciphertext is included — this operation exists to enumerate what is held, and reading a value is a separate, per-secret call.  Scoped to the caller's own org and nothing else, structurally: there is no org in the path, the store root is derived from the validated org claim, and a caller therefore has no way to name another tenant's namespace. `path` narrows to a subpath and `env` selects the environment; both are also accepted under the operator's spellings, `secretPath` and `environment`. An omitted `env` means every environment and an omitted `path` means the whole org, because a default here reported a populated store as empty.  Admission is fail-closed and in order: a validated member, an org that is a DNS-1123 label, and a store holding a master key — 403, 400 and 503 respectively, all decided before any record is touched.
132pub async fn get_kms_secrets(configuration: &configuration::Configuration, env: Option<&str>, environment: Option<&str>, path: Option<&str>, secret_path: Option<&str>) -> Result<models::KmsSecrets, Error<GetKmsSecretsError>> {
133    // add a prefix to parameters to efficiently prevent name collisions
134    let p_env = env;
135    let p_environment = environment;
136    let p_path = path;
137    let p_secret_path = secret_path;
138
139    let uri_str = format!("{}/v1/kms/secrets", configuration.base_path);
140    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
141
142    if let Some(ref param_value) = p_env {
143        req_builder = req_builder.query(&[("env", &param_value.to_string())]);
144    }
145    if let Some(ref param_value) = p_environment {
146        req_builder = req_builder.query(&[("environment", &param_value.to_string())]);
147    }
148    if let Some(ref param_value) = p_path {
149        req_builder = req_builder.query(&[("path", &param_value.to_string())]);
150    }
151    if let Some(ref param_value) = p_secret_path {
152        req_builder = req_builder.query(&[("secretPath", &param_value.to_string())]);
153    }
154    if let Some(ref user_agent) = configuration.user_agent {
155        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
156    }
157    if let Some(ref token) = configuration.bearer_access_token {
158        req_builder = req_builder.bearer_auth(token.to_owned());
159    };
160
161    let req = req_builder.build()?;
162    let resp = configuration.client.execute(req).await?;
163
164    let status = resp.status();
165    let content_type = resp
166        .headers()
167        .get("content-type")
168        .and_then(|v| v.to_str().ok())
169        .unwrap_or("application/octet-stream");
170    let content_type = super::ContentType::from(content_type);
171
172    if !status.is_client_error() && !status.is_server_error() {
173        let content = resp.text().await?;
174        match content_type {
175            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
176            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::KmsSecrets`"))),
177            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::KmsSecrets`")))),
178        }
179    } else {
180        let content = resp.text().await?;
181        let entity: Option<GetKmsSecretsError> = serde_json::from_str(&content).ok();
182        Err(Error::ResponseError(ResponseContent { status, content, entity }))
183    }
184}
185
186/// Exchanges a machine credential for an IAM bearer token.  Takes a tenant's machine credential — a client id and client secret — and returns an owner-scoped IAM access token with its lifetime, which is the bearer the caller then carries on the org-scoped secret operations.  It is deliberately public and unauthenticated, because it IS the credential exchange and runs before any principal exists. That makes it the one route in this subsystem rate-limited PER SOURCE IP, keyed on the real TCP peer rather than on any caller-supplied header, and body-capped in the same place.  The submitted secret is never logged and never echoed, and failures collapse to one clean status with no upstream detail: 401 when the credential does not authenticate, 502 when the identity provider is unreachable, 503 when no issuer is configured. That is on purpose — a richer error would be a validity oracle for guessed credentials.
187pub async fn post_kms_auth_login(configuration: &configuration::Configuration, kms_login: models::KmsLogin) -> Result<models::KmsToken, Error<PostKmsAuthLoginError>> {
188    // add a prefix to parameters to efficiently prevent name collisions
189    let p_kms_login = kms_login;
190
191    let uri_str = format!("{}/v1/kms/auth/login", configuration.base_path);
192    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
193
194    if let Some(ref user_agent) = configuration.user_agent {
195        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
196    }
197    if let Some(ref token) = configuration.bearer_access_token {
198        req_builder = req_builder.bearer_auth(token.to_owned());
199    };
200    req_builder = req_builder.json(&p_kms_login);
201
202    let req = req_builder.build()?;
203    let resp = configuration.client.execute(req).await?;
204
205    let status = resp.status();
206    let content_type = resp
207        .headers()
208        .get("content-type")
209        .and_then(|v| v.to_str().ok())
210        .unwrap_or("application/octet-stream");
211    let content_type = super::ContentType::from(content_type);
212
213    if !status.is_client_error() && !status.is_server_error() {
214        let content = resp.text().await?;
215        match content_type {
216            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
217            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::KmsToken`"))),
218            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::KmsToken`")))),
219        }
220    } else {
221        let content = resp.text().await?;
222        let entity: Option<PostKmsAuthLoginError> = serde_json::from_str(&content).ok();
223        Err(Error::ResponseError(ResponseContent { status, content, entity }))
224    }
225}
226
227/// Stores or replaces one secret in your org.  Upserts one secret under the caller's own org. The value is sealed before it is written — a fresh per-secret data key, itself wrapped by the master key — so plaintext never reaches disk. The receipt confirms the name and environment that were written and does not echo the value.  `env` is REQUIRED on a write and has no default, which is the rule most easily got wrong here: reads and deletes still fall back to the default environment for older callers, but a write must not, because the environment is part of the storage key. A silently defaulted write lands in a bucket the readers that resolve project, environment and path never look in, and the stale value keeps being served — so the write fails loudly instead.  `name` is required, `path` is an optional subpath beneath the org root, and the org is taken from the validated claim rather than the body.  Requires ADMIN authority over the org — a member reads, an admin writes. A machine credential holds no membership and so is never an org admin: it can read the secrets it was issued for and cannot replace one. Fail-closed admission, in order: admin of the org, well-formed org, master key present — 403, 400 and 503, all decided before any record is touched.
228pub async fn post_kms_secrets(configuration: &configuration::Configuration, kms_put: models::KmsPut) -> Result<models::KmsStored, Error<PostKmsSecretsError>> {
229    // add a prefix to parameters to efficiently prevent name collisions
230    let p_kms_put = kms_put;
231
232    let uri_str = format!("{}/v1/kms/secrets", configuration.base_path);
233    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
234
235    if let Some(ref user_agent) = configuration.user_agent {
236        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
237    }
238    if let Some(ref token) = configuration.bearer_access_token {
239        req_builder = req_builder.bearer_auth(token.to_owned());
240    };
241    req_builder = req_builder.json(&p_kms_put);
242
243    let req = req_builder.build()?;
244    let resp = configuration.client.execute(req).await?;
245
246    let status = resp.status();
247    let content_type = resp
248        .headers()
249        .get("content-type")
250        .and_then(|v| v.to_str().ok())
251        .unwrap_or("application/octet-stream");
252    let content_type = super::ContentType::from(content_type);
253
254    if !status.is_client_error() && !status.is_server_error() {
255        let content = resp.text().await?;
256        match content_type {
257            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
258            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::KmsStored`"))),
259            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::KmsStored`")))),
260        }
261    } else {
262        let content = resp.text().await?;
263        let entity: Option<PostKmsSecretsError> = serde_json::from_str(&content).ok();
264        Err(Error::ResponseError(ResponseContent { status, content, entity }))
265    }
266}
267