1use derive_builder::Builder;
29use http::{HeaderMap, HeaderName, HeaderValue};
30
31use openstack_sdk_core::api::rest_endpoint_prelude::*;
32
33use openstack_sdk_core::api::common::serialize_sensitive_optional_string;
34use openstack_sdk_core::api::common::serialize_sensitive_string;
35use secrecy::SecretString;
36use serde::Deserialize;
37use serde::Serialize;
38use std::borrow::Cow;
39
40#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
42#[builder(setter(strip_option))]
43pub struct Domain<'a> {
44 #[serde(skip_serializing_if = "Option::is_none")]
46 #[builder(default, setter(into))]
47 pub(crate) id: Option<Cow<'a, str>>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 #[builder(default, setter(into))]
52 pub(crate) name: Option<Cow<'a, str>>,
53}
54
55#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
58#[builder(setter(strip_option))]
59pub struct User<'a> {
60 #[serde(skip_serializing_if = "Option::is_none")]
62 #[builder(default, setter(into))]
63 pub(crate) domain: Option<Domain<'a>>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 #[builder(default, setter(into))]
68 pub(crate) id: Option<Cow<'a, str>>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 #[builder(default, setter(into))]
73 pub(crate) name: Option<Cow<'a, str>>,
74}
75
76#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
78#[builder(setter(strip_option))]
79pub struct ApplicationCredential<'a> {
80 #[serde(skip_serializing_if = "Option::is_none")]
84 #[builder(default, setter(into))]
85 pub(crate) id: Option<Cow<'a, str>>,
86
87 #[serde(skip_serializing_if = "Option::is_none")]
90 #[builder(default, setter(into))]
91 pub(crate) name: Option<Cow<'a, str>>,
92
93 #[serde(serialize_with = "serialize_sensitive_string")]
95 #[builder(setter(into))]
96 pub(crate) secret: SecretString,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
101 #[builder(default, setter(into))]
102 pub(crate) user: Option<User<'a>>,
103}
104
105#[derive(Debug, Deserialize, Clone, Serialize)]
106pub enum Methods {
107 #[serde(rename = "application_credential")]
108 ApplicationCredential,
109 #[serde(rename = "password")]
110 Password,
111 #[serde(rename = "token")]
112 Token,
113 #[serde(rename = "totp")]
114 Totp,
115}
116
117#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
119#[builder(setter(strip_option))]
120pub struct PasswordUser<'a> {
121 #[serde(skip_serializing_if = "Option::is_none")]
123 #[builder(default, setter(into))]
124 pub(crate) domain: Option<Domain<'a>>,
125
126 #[serde(skip_serializing_if = "Option::is_none")]
128 #[builder(default, setter(into))]
129 pub(crate) id: Option<Cow<'a, str>>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
135 #[builder(default, setter(into))]
136 pub(crate) name: Option<Cow<'a, str>>,
137
138 #[serde(
140 serialize_with = "serialize_sensitive_optional_string",
141 skip_serializing_if = "Option::is_none"
142 )]
143 #[builder(default, setter(into))]
144 pub(crate) password: Option<SecretString>,
145}
146
147#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
149#[builder(setter(strip_option))]
150pub struct Password<'a> {
151 #[serde(skip_serializing_if = "Option::is_none")]
153 #[builder(default, setter(into))]
154 pub(crate) user: Option<PasswordUser<'a>>,
155}
156
157#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
159#[builder(setter(strip_option))]
160pub struct Token {
161 #[serde(serialize_with = "serialize_sensitive_string")]
163 #[builder(setter(into))]
164 pub(crate) id: SecretString,
165}
166
167#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
168#[builder(setter(strip_option))]
169pub struct TotpUser<'a> {
170 #[serde(skip_serializing_if = "Option::is_none")]
172 #[builder(default, setter(into))]
173 pub(crate) domain: Option<Domain<'a>>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 #[builder(default, setter(into))]
178 pub(crate) id: Option<Cow<'a, str>>,
179
180 #[serde(skip_serializing_if = "Option::is_none")]
182 #[builder(default, setter(into))]
183 pub(crate) name: Option<Cow<'a, str>>,
184
185 #[serde(serialize_with = "serialize_sensitive_string")]
187 #[builder(setter(into))]
188 pub(crate) passcode: SecretString,
189}
190
191#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
193#[builder(setter(strip_option))]
194pub struct Totp<'a> {
195 #[serde()]
196 #[builder(setter(into))]
197 pub(crate) user: TotpUser<'a>,
198}
199
200#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
202#[builder(setter(strip_option))]
203pub struct Identity<'a> {
204 #[serde(skip_serializing_if = "Option::is_none")]
206 #[builder(default, setter(into))]
207 pub(crate) application_credential: Option<ApplicationCredential<'a>>,
208
209 #[serde()]
212 #[builder(setter(into))]
213 pub(crate) methods: Vec<Methods>,
214
215 #[serde(skip_serializing_if = "Option::is_none")]
217 #[builder(default, setter(into))]
218 pub(crate) password: Option<Password<'a>>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 #[builder(default, setter(into))]
223 pub(crate) token: Option<Token>,
224
225 #[serde(skip_serializing_if = "Option::is_none")]
227 #[builder(default, setter(into))]
228 pub(crate) totp: Option<Totp<'a>>,
229}
230
231#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
232#[builder(setter(strip_option))]
233pub struct OsTrustTrust<'a> {
234 #[serde(skip_serializing_if = "Option::is_none")]
235 #[builder(default, setter(into))]
236 pub(crate) id: Option<Cow<'a, str>>,
237}
238
239#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
240#[builder(setter(strip_option))]
241pub struct ScopeDomain<'a> {
242 #[serde(skip_serializing_if = "Option::is_none")]
244 #[builder(default, setter(into))]
245 pub(crate) id: Option<Cow<'a, str>>,
246
247 #[serde(skip_serializing_if = "Option::is_none")]
249 #[builder(default, setter(into))]
250 pub(crate) name: Option<Cow<'a, str>>,
251}
252
253#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
254#[builder(setter(strip_option))]
255pub struct ProjectDomain<'a> {
256 #[serde(skip_serializing_if = "Option::is_none")]
258 #[builder(default, setter(into))]
259 pub(crate) id: Option<Cow<'a, str>>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 #[builder(default, setter(into))]
264 pub(crate) name: Option<Cow<'a, str>>,
265}
266
267#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
268#[builder(setter(strip_option))]
269pub struct Project<'a> {
270 #[serde(skip_serializing_if = "Option::is_none")]
271 #[builder(default, setter(into))]
272 pub(crate) domain: Option<ProjectDomain<'a>>,
273
274 #[serde(skip_serializing_if = "Option::is_none")]
276 #[builder(default, setter(into))]
277 pub(crate) id: Option<Cow<'a, str>>,
278
279 #[serde(skip_serializing_if = "Option::is_none")]
281 #[builder(default, setter(into))]
282 pub(crate) name: Option<Cow<'a, str>>,
283}
284
285#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
286#[builder(setter(strip_option))]
287pub struct System {
288 #[serde(skip_serializing_if = "Option::is_none")]
289 #[builder(default, setter(into))]
290 pub(crate) all: Option<bool>,
291}
292
293#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
303#[builder(setter(strip_option))]
304pub struct Scope<'a> {
305 #[serde(skip_serializing_if = "Option::is_none")]
306 #[builder(default, setter(into))]
307 pub(crate) domain: Option<ScopeDomain<'a>>,
308
309 #[serde(rename = "OS-TRUST:trust", skip_serializing_if = "Option::is_none")]
310 #[builder(default, setter(into))]
311 pub(crate) os_trust_trust: Option<OsTrustTrust<'a>>,
312
313 #[serde(skip_serializing_if = "Option::is_none")]
314 #[builder(default, setter(into))]
315 pub(crate) project: Option<Project<'a>>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
318 #[builder(default, setter(into))]
319 pub(crate) system: Option<System>,
320}
321
322#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
324#[builder(setter(strip_option))]
325pub struct Auth<'a> {
326 #[serde()]
328 #[builder(setter(into))]
329 pub(crate) identity: Identity<'a>,
330
331 #[serde(skip_serializing_if = "Option::is_none")]
341 #[builder(default, setter(into))]
342 pub(crate) scope: Option<Scope<'a>>,
343}
344
345#[derive(Builder, Debug, Clone)]
346#[builder(setter(strip_option))]
347pub struct Request<'a> {
348 #[builder(setter(into))]
350 pub(crate) auth: Auth<'a>,
351
352 #[builder(setter(name = "_headers"), default, private)]
353 _headers: Option<HeaderMap>,
354}
355impl<'a> Request<'a> {
356 pub fn builder() -> RequestBuilder<'a> {
358 RequestBuilder::default()
359 }
360}
361
362impl<'a> RequestBuilder<'a> {
363 pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
365 where
366 K: Into<HeaderName>,
367 V: Into<HeaderValue>,
368 {
369 self._headers
370 .get_or_insert(None)
371 .get_or_insert_with(HeaderMap::new)
372 .insert(header_name.into(), header_value.into());
373 self
374 }
375
376 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
378 where
379 I: Iterator<Item = T>,
380 T: Into<(Option<HeaderName>, HeaderValue)>,
381 {
382 self._headers
383 .get_or_insert(None)
384 .get_or_insert_with(HeaderMap::new)
385 .extend(iter.map(Into::into));
386 self
387 }
388}
389
390impl RestEndpoint for Request<'_> {
391 fn method(&self) -> http::Method {
392 http::Method::POST
393 }
394
395 fn endpoint(&self) -> Cow<'static, str> {
396 "auth/tokens".to_string().into()
397 }
398
399 fn parameters(&self) -> QueryParams<'_> {
400 QueryParams::default()
401 }
402
403 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
404 let mut params = JsonBodyParams::default();
405
406 params.push("auth", serde_json::to_value(&self.auth)?);
407
408 params.into_body()
409 }
410
411 fn service_type(&self) -> ServiceType {
412 ServiceType::Identity
413 }
414
415 fn response_key(&self) -> Option<Cow<'static, str>> {
416 Some("token".into())
417 }
418
419 fn request_headers(&self) -> Option<&HeaderMap> {
421 self._headers.as_ref()
422 }
423
424 fn api_version(&self) -> Option<ApiVersion> {
426 Some(ApiVersion::new(3, 0))
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433 use http::{HeaderName, HeaderValue};
434 use httpmock::MockServer;
435 #[cfg(feature = "sync")]
436 use openstack_sdk_core::api::Query;
437 use openstack_sdk_core::test::client::FakeOpenStackClient;
438 use openstack_sdk_core::types::ServiceType;
439 use serde_json::json;
440
441 #[test]
442 fn test_service_type() {
443 assert_eq!(
444 Request::builder()
445 .auth(
446 AuthBuilder::default()
447 .identity(
448 IdentityBuilder::default()
449 .methods(Vec::from([Methods::ApplicationCredential]))
450 .build()
451 .unwrap()
452 )
453 .build()
454 .unwrap()
455 )
456 .build()
457 .unwrap()
458 .service_type(),
459 ServiceType::Identity
460 );
461 }
462
463 #[test]
464 fn test_response_key() {
465 assert_eq!(
466 Request::builder()
467 .auth(
468 AuthBuilder::default()
469 .identity(
470 IdentityBuilder::default()
471 .methods(Vec::from([Methods::ApplicationCredential]))
472 .build()
473 .unwrap()
474 )
475 .build()
476 .unwrap()
477 )
478 .build()
479 .unwrap()
480 .response_key()
481 .unwrap(),
482 "token"
483 );
484 }
485
486 #[cfg(feature = "sync")]
487 #[test]
488 fn endpoint() {
489 let server = MockServer::start();
490 let client = FakeOpenStackClient::new(server.base_url());
491 let mock = server.mock(|when, then| {
492 when.method(httpmock::Method::POST)
493 .path("/auth/tokens".to_string());
494
495 then.status(200)
496 .header("content-type", "application/json")
497 .json_body(json!({ "token": {} }));
498 });
499
500 let endpoint = Request::builder()
501 .auth(
502 AuthBuilder::default()
503 .identity(
504 IdentityBuilder::default()
505 .methods(Vec::from([Methods::ApplicationCredential]))
506 .build()
507 .unwrap(),
508 )
509 .build()
510 .unwrap(),
511 )
512 .build()
513 .unwrap();
514 let _: serde_json::Value = endpoint.query(&client).unwrap();
515 mock.assert();
516 }
517
518 #[cfg(feature = "sync")]
519 #[test]
520 fn endpoint_headers() {
521 let server = MockServer::start();
522 let client = FakeOpenStackClient::new(server.base_url());
523 let mock = server.mock(|when, then| {
524 when.method(httpmock::Method::POST)
525 .path("/auth/tokens".to_string())
526 .header("foo", "bar")
527 .header("not_foo", "not_bar");
528 then.status(200)
529 .header("content-type", "application/json")
530 .json_body(json!({ "token": {} }));
531 });
532
533 let endpoint = Request::builder()
534 .auth(
535 AuthBuilder::default()
536 .identity(
537 IdentityBuilder::default()
538 .methods(Vec::from([Methods::ApplicationCredential]))
539 .build()
540 .unwrap(),
541 )
542 .build()
543 .unwrap(),
544 )
545 .headers(
546 [(
547 Some(HeaderName::from_static("foo")),
548 HeaderValue::from_static("bar"),
549 )]
550 .into_iter(),
551 )
552 .header(
553 HeaderName::from_static("not_foo"),
554 HeaderValue::from_static("not_bar"),
555 )
556 .build()
557 .unwrap();
558 let _: serde_json::Value = endpoint.query(&client).unwrap();
559 mock.assert();
560 }
561}