openstack_sdk/api/identity/v3/auth/token/
create.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Authenticates an identity and generates a token. Uses the password
19//! authentication method. Authorization is unscoped.
20//!
21//! The request body must include a payload that specifies the authentication
22//! method, which is `password`, and the user, by ID or name, and password
23//! credentials.
24//!
25//! Relationship:
26//! `https://docs.openstack.org/api/openstack-identity/3/rel/auth_tokens`
27//!
28use derive_builder::Builder;
29use http::{HeaderMap, HeaderName, HeaderValue};
30
31use crate::api::rest_endpoint_prelude::*;
32
33use crate::api::common::serialize_sensitive_optional_string;
34use crate::api::common::serialize_sensitive_string;
35use secrecy::SecretString;
36use serde::Deserialize;
37use serde::Serialize;
38use std::borrow::Cow;
39
40#[derive(Debug, Deserialize, Clone, Serialize)]
41pub enum Methods {
42    #[serde(rename = "application_credential")]
43    ApplicationCredential,
44    #[serde(rename = "password")]
45    Password,
46    #[serde(rename = "token")]
47    Token,
48    #[serde(rename = "totp")]
49    Totp,
50}
51
52/// A `domain` object
53#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
54#[builder(setter(strip_option))]
55pub struct Domain<'a> {
56    /// User Domain ID
57    #[serde(skip_serializing_if = "Option::is_none")]
58    #[builder(default, setter(into))]
59    pub(crate) id: Option<Cow<'a, str>>,
60
61    /// User Domain Name
62    #[serde(skip_serializing_if = "Option::is_none")]
63    #[builder(default, setter(into))]
64    pub(crate) name: Option<Cow<'a, str>>,
65}
66
67/// A `user` object.
68#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
69#[builder(setter(strip_option))]
70pub struct User<'a> {
71    /// A `domain` object
72    #[serde(skip_serializing_if = "Option::is_none")]
73    #[builder(default, setter(into))]
74    pub(crate) domain: Option<Domain<'a>>,
75
76    /// The ID of the user. Required if you do not specify the user name.
77    #[serde(skip_serializing_if = "Option::is_none")]
78    #[builder(default, setter(into))]
79    pub(crate) id: Option<Cow<'a, str>>,
80
81    /// The user name. Required if you do not specify the ID of the user. If
82    /// you specify the user name, you must also specify the domain, by ID or
83    /// name.
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[builder(default, setter(into))]
86    pub(crate) name: Option<Cow<'a, str>>,
87
88    /// User Password
89    #[serde(
90        serialize_with = "serialize_sensitive_optional_string",
91        skip_serializing_if = "Option::is_none"
92    )]
93    #[builder(default, setter(into))]
94    pub(crate) password: Option<SecretString>,
95}
96
97/// The `password` object, contains the authentication information.
98#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
99#[builder(setter(strip_option))]
100pub struct Password<'a> {
101    /// A `user` object.
102    #[serde(skip_serializing_if = "Option::is_none")]
103    #[builder(default, setter(into))]
104    pub(crate) user: Option<User<'a>>,
105}
106
107/// A `token` object
108#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
109#[builder(setter(strip_option))]
110pub struct Token {
111    /// Authorization Token value
112    #[serde(serialize_with = "serialize_sensitive_string")]
113    #[builder(setter(into))]
114    pub(crate) id: SecretString,
115}
116
117#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
118#[builder(setter(strip_option))]
119pub struct TotpUser<'a> {
120    /// A `domain` object
121    #[serde(skip_serializing_if = "Option::is_none")]
122    #[builder(default, setter(into))]
123    pub(crate) domain: Option<Domain<'a>>,
124
125    /// The user ID
126    #[serde(skip_serializing_if = "Option::is_none")]
127    #[builder(default, setter(into))]
128    pub(crate) id: Option<Cow<'a, str>>,
129
130    /// The user name
131    #[serde(skip_serializing_if = "Option::is_none")]
132    #[builder(default, setter(into))]
133    pub(crate) name: Option<Cow<'a, str>>,
134
135    /// MFA passcode
136    #[serde(serialize_with = "serialize_sensitive_string")]
137    #[builder(setter(into))]
138    pub(crate) passcode: SecretString,
139}
140
141/// Multi Factor Authentication information
142#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
143#[builder(setter(strip_option))]
144pub struct Totp<'a> {
145    #[serde()]
146    #[builder(setter(into))]
147    pub(crate) user: TotpUser<'a>,
148}
149
150/// A user object, required if an application credential is identified by name
151/// and not ID.
152#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
153#[builder(setter(strip_option))]
154pub struct ApplicationCredentialUser<'a> {
155    /// A `domain` object
156    #[serde(skip_serializing_if = "Option::is_none")]
157    #[builder(default, setter(into))]
158    pub(crate) domain: Option<Domain<'a>>,
159
160    /// The user ID
161    #[serde(skip_serializing_if = "Option::is_none")]
162    #[builder(default, setter(into))]
163    pub(crate) id: Option<Cow<'a, str>>,
164
165    /// The user name
166    #[serde(skip_serializing_if = "Option::is_none")]
167    #[builder(default, setter(into))]
168    pub(crate) name: Option<Cow<'a, str>>,
169}
170
171/// An application credential object.
172#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
173#[builder(setter(strip_option))]
174pub struct ApplicationCredential<'a> {
175    #[serde(skip_serializing_if = "Option::is_none")]
176    #[builder(default, setter(into))]
177    pub(crate) id: Option<Cow<'a, str>>,
178
179    #[serde(skip_serializing_if = "Option::is_none")]
180    #[builder(default, setter(into))]
181    pub(crate) name: Option<Cow<'a, str>>,
182
183    /// The secret for authenticating the application credential.
184    #[serde(serialize_with = "serialize_sensitive_string")]
185    #[builder(setter(into))]
186    pub(crate) secret: SecretString,
187
188    /// A user object, required if an application credential is identified by
189    /// name and not ID.
190    #[serde(skip_serializing_if = "Option::is_none")]
191    #[builder(default, setter(into))]
192    pub(crate) user: Option<ApplicationCredentialUser<'a>>,
193}
194
195/// An `identity` object.
196#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
197#[builder(setter(strip_option))]
198pub struct Identity<'a> {
199    /// An application credential object.
200    #[serde(skip_serializing_if = "Option::is_none")]
201    #[builder(default, setter(into))]
202    pub(crate) application_credential: Option<ApplicationCredential<'a>>,
203
204    /// The authentication method. For password authentication, specify
205    /// `password`.
206    #[serde()]
207    #[builder(setter(into))]
208    pub(crate) methods: Vec<Methods>,
209
210    /// The `password` object, contains the authentication information.
211    #[serde(skip_serializing_if = "Option::is_none")]
212    #[builder(default, setter(into))]
213    pub(crate) password: Option<Password<'a>>,
214
215    /// A `token` object
216    #[serde(skip_serializing_if = "Option::is_none")]
217    #[builder(default, setter(into))]
218    pub(crate) token: Option<Token>,
219
220    /// Multi Factor Authentication information
221    #[serde(skip_serializing_if = "Option::is_none")]
222    #[builder(default, setter(into))]
223    pub(crate) totp: Option<Totp<'a>>,
224}
225
226#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
227#[builder(setter(strip_option))]
228pub struct ProjectDomain<'a> {
229    /// Project domain Id
230    #[serde(skip_serializing_if = "Option::is_none")]
231    #[builder(default, setter(into))]
232    pub(crate) id: Option<Cow<'a, str>>,
233
234    /// Project domain Name
235    #[serde(skip_serializing_if = "Option::is_none")]
236    #[builder(default, setter(into))]
237    pub(crate) name: Option<Cow<'a, str>>,
238}
239
240#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
241#[builder(setter(strip_option))]
242pub struct Project<'a> {
243    #[serde(skip_serializing_if = "Option::is_none")]
244    #[builder(default, setter(into))]
245    pub(crate) domain: Option<ProjectDomain<'a>>,
246
247    /// Project Id
248    #[serde(skip_serializing_if = "Option::is_none")]
249    #[builder(default, setter(into))]
250    pub(crate) id: Option<Cow<'a, str>>,
251
252    /// Project Name
253    #[serde(skip_serializing_if = "Option::is_none")]
254    #[builder(default, setter(into))]
255    pub(crate) name: Option<Cow<'a, str>>,
256}
257
258#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
259#[builder(setter(strip_option))]
260pub struct ScopeDomain<'a> {
261    /// Domain id
262    #[serde(skip_serializing_if = "Option::is_none")]
263    #[builder(default, setter(into))]
264    pub(crate) id: Option<Cow<'a, str>>,
265
266    /// Domain name
267    #[serde(skip_serializing_if = "Option::is_none")]
268    #[builder(default, setter(into))]
269    pub(crate) name: Option<Cow<'a, str>>,
270}
271
272#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
273#[builder(setter(strip_option))]
274pub struct OsTrustTrust<'a> {
275    #[serde(skip_serializing_if = "Option::is_none")]
276    #[builder(default, setter(into))]
277    pub(crate) id: Option<Cow<'a, str>>,
278}
279
280#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
281#[builder(setter(strip_option))]
282pub struct System {
283    #[serde(skip_serializing_if = "Option::is_none")]
284    #[builder(default, setter(into))]
285    pub(crate) all: Option<bool>,
286}
287
288/// The authorization scope, including the system (Since v3.10), a project, or
289/// a domain (Since v3.4). If multiple scopes are specified in the same request
290/// (e.g. project and domain or domain and system) an HTTP 400 Bad Request will
291/// be returned, as a token cannot be simultaneously scoped to multiple
292/// authorization targets. An ID is sufficient to uniquely identify a project
293/// but if a project is specified by name, then the domain of the project must
294/// also be specified in order to uniquely identify the project by name. A
295/// domain scope may be specified by either the domain’s ID or name with
296/// equivalent results.
297#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
298#[builder(setter(strip_option))]
299pub struct Scope<'a> {
300    #[serde(skip_serializing_if = "Option::is_none")]
301    #[builder(default, setter(into))]
302    pub(crate) domain: Option<ScopeDomain<'a>>,
303
304    #[serde(rename = "OS-TRUST:trust", skip_serializing_if = "Option::is_none")]
305    #[builder(default, setter(into))]
306    pub(crate) os_trust_trust: Option<OsTrustTrust<'a>>,
307
308    #[serde(skip_serializing_if = "Option::is_none")]
309    #[builder(default, setter(into))]
310    pub(crate) project: Option<Project<'a>>,
311
312    #[serde(skip_serializing_if = "Option::is_none")]
313    #[builder(default, setter(into))]
314    pub(crate) system: Option<System>,
315}
316
317/// An `auth` object.
318#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
319#[builder(setter(strip_option))]
320pub struct Auth<'a> {
321    /// An `identity` object.
322    #[serde()]
323    #[builder(setter(into))]
324    pub(crate) identity: Identity<'a>,
325
326    /// The authorization scope, including the system (Since v3.10), a project,
327    /// or a domain (Since v3.4). If multiple scopes are specified in the same
328    /// request (e.g. project and domain or domain and system) an HTTP 400 Bad
329    /// Request will be returned, as a token cannot be simultaneously scoped to
330    /// multiple authorization targets. An ID is sufficient to uniquely
331    /// identify a project but if a project is specified by name, then the
332    /// domain of the project must also be specified in order to uniquely
333    /// identify the project by name. A domain scope may be specified by either
334    /// the domain’s ID or name with equivalent results.
335    #[serde(skip_serializing_if = "Option::is_none")]
336    #[builder(default, setter(into))]
337    pub(crate) scope: Option<Scope<'a>>,
338}
339
340#[derive(Builder, Debug, Clone)]
341#[builder(setter(strip_option))]
342pub struct Request<'a> {
343    /// An `auth` object.
344    #[builder(setter(into))]
345    pub(crate) auth: Auth<'a>,
346
347    #[builder(setter(name = "_headers"), default, private)]
348    _headers: Option<HeaderMap>,
349}
350impl<'a> Request<'a> {
351    /// Create a builder for the endpoint.
352    pub fn builder() -> RequestBuilder<'a> {
353        RequestBuilder::default()
354    }
355}
356
357impl RequestBuilder<'_> {
358    /// Add a single header to the Token.
359    pub fn header(&mut self, header_name: &'static str, header_value: &'static str) -> &mut Self
360where {
361        self._headers
362            .get_or_insert(None)
363            .get_or_insert_with(HeaderMap::new)
364            .insert(header_name, HeaderValue::from_static(header_value));
365        self
366    }
367
368    /// Add multiple headers.
369    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
370    where
371        I: Iterator<Item = T>,
372        T: Into<(Option<HeaderName>, HeaderValue)>,
373    {
374        self._headers
375            .get_or_insert(None)
376            .get_or_insert_with(HeaderMap::new)
377            .extend(iter.map(Into::into));
378        self
379    }
380}
381
382impl RestEndpoint for Request<'_> {
383    fn method(&self) -> http::Method {
384        http::Method::POST
385    }
386
387    fn endpoint(&self) -> Cow<'static, str> {
388        "auth/tokens".to_string().into()
389    }
390
391    fn parameters(&self) -> QueryParams {
392        QueryParams::default()
393    }
394
395    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
396        let mut params = JsonBodyParams::default();
397
398        params.push("auth", serde_json::to_value(&self.auth)?);
399
400        params.into_body()
401    }
402
403    fn service_type(&self) -> ServiceType {
404        ServiceType::Identity
405    }
406
407    fn response_key(&self) -> Option<Cow<'static, str>> {
408        Some("token".into())
409    }
410
411    /// Returns headers to be set into the request
412    fn request_headers(&self) -> Option<&HeaderMap> {
413        self._headers.as_ref()
414    }
415
416    /// Returns required API version
417    fn api_version(&self) -> Option<ApiVersion> {
418        Some(ApiVersion::new(3, 0))
419    }
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425    #[cfg(feature = "sync")]
426    use crate::api::Query;
427    use crate::test::client::FakeOpenStackClient;
428    use crate::types::ServiceType;
429    use http::{HeaderName, HeaderValue};
430    use httpmock::MockServer;
431    use serde_json::json;
432
433    #[test]
434    fn test_service_type() {
435        assert_eq!(
436            Request::builder()
437                .auth(
438                    AuthBuilder::default()
439                        .identity(
440                            IdentityBuilder::default()
441                                .methods(Vec::from([Methods::ApplicationCredential]))
442                                .build()
443                                .unwrap()
444                        )
445                        .build()
446                        .unwrap()
447                )
448                .build()
449                .unwrap()
450                .service_type(),
451            ServiceType::Identity
452        );
453    }
454
455    #[test]
456    fn test_response_key() {
457        assert_eq!(
458            Request::builder()
459                .auth(
460                    AuthBuilder::default()
461                        .identity(
462                            IdentityBuilder::default()
463                                .methods(Vec::from([Methods::ApplicationCredential]))
464                                .build()
465                                .unwrap()
466                        )
467                        .build()
468                        .unwrap()
469                )
470                .build()
471                .unwrap()
472                .response_key()
473                .unwrap(),
474            "token"
475        );
476    }
477
478    #[cfg(feature = "sync")]
479    #[test]
480    fn endpoint() {
481        let server = MockServer::start();
482        let client = FakeOpenStackClient::new(server.base_url());
483        let mock = server.mock(|when, then| {
484            when.method(httpmock::Method::POST)
485                .path("/auth/tokens".to_string());
486
487            then.status(200)
488                .header("content-type", "application/json")
489                .json_body(json!({ "token": {} }));
490        });
491
492        let endpoint = Request::builder()
493            .auth(
494                AuthBuilder::default()
495                    .identity(
496                        IdentityBuilder::default()
497                            .methods(Vec::from([Methods::ApplicationCredential]))
498                            .build()
499                            .unwrap(),
500                    )
501                    .build()
502                    .unwrap(),
503            )
504            .build()
505            .unwrap();
506        let _: serde_json::Value = endpoint.query(&client).unwrap();
507        mock.assert();
508    }
509
510    #[cfg(feature = "sync")]
511    #[test]
512    fn endpoint_headers() {
513        let server = MockServer::start();
514        let client = FakeOpenStackClient::new(server.base_url());
515        let mock = server.mock(|when, then| {
516            when.method(httpmock::Method::POST)
517                .path("/auth/tokens".to_string())
518                .header("foo", "bar")
519                .header("not_foo", "not_bar");
520            then.status(200)
521                .header("content-type", "application/json")
522                .json_body(json!({ "token": {} }));
523        });
524
525        let endpoint = Request::builder()
526            .auth(
527                AuthBuilder::default()
528                    .identity(
529                        IdentityBuilder::default()
530                            .methods(Vec::from([Methods::ApplicationCredential]))
531                            .build()
532                            .unwrap(),
533                    )
534                    .build()
535                    .unwrap(),
536            )
537            .headers(
538                [(
539                    Some(HeaderName::from_static("foo")),
540                    HeaderValue::from_static("bar"),
541                )]
542                .into_iter(),
543            )
544            .header("not_foo", "not_bar")
545            .build()
546            .unwrap();
547        let _: serde_json::Value = endpoint.query(&client).unwrap();
548        mock.assert();
549    }
550}