Skip to main content

openstack_sdk_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 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/// A `domain` object
41#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
42#[builder(setter(strip_option))]
43pub struct Domain<'a> {
44    /// User Domain ID
45    #[serde(skip_serializing_if = "Option::is_none")]
46    #[builder(default, setter(into))]
47    pub(crate) id: Option<Cow<'a, str>>,
48
49    /// User Domain Name
50    #[serde(skip_serializing_if = "Option::is_none")]
51    #[builder(default, setter(into))]
52    pub(crate) name: Option<Cow<'a, str>>,
53}
54
55/// A user object, required if an application credential is identified by name
56/// and not ID.
57#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
58#[builder(setter(strip_option))]
59pub struct User<'a> {
60    /// A `domain` object
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[builder(default, setter(into))]
63    pub(crate) domain: Option<Domain<'a>>,
64
65    /// The user ID
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[builder(default, setter(into))]
68    pub(crate) id: Option<Cow<'a, str>>,
69
70    /// The user name
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[builder(default, setter(into))]
73    pub(crate) name: Option<Cow<'a, str>>,
74}
75
76/// An application credential object.
77#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
78#[builder(setter(strip_option))]
79pub struct ApplicationCredential<'a> {
80    /// The ID of the application credential used for authentication. If not
81    /// provided, the application credential must be identified by its name and
82    /// its owning user.
83    #[serde(skip_serializing_if = "Option::is_none")]
84    #[builder(default, setter(into))]
85    pub(crate) id: Option<Cow<'a, str>>,
86
87    /// The name of the application credential used for authentication. If
88    /// provided, must be accompanied by a user object.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    #[builder(default, setter(into))]
91    pub(crate) name: Option<Cow<'a, str>>,
92
93    /// The secret for authenticating the application credential.
94    #[serde(serialize_with = "serialize_sensitive_string")]
95    #[builder(setter(into))]
96    pub(crate) secret: SecretString,
97
98    /// A user object, required if an application credential is identified by
99    /// name and not ID.
100    #[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/// A `user` object.
118#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
119#[builder(setter(strip_option))]
120pub struct PasswordUser<'a> {
121    /// A `domain` object
122    #[serde(skip_serializing_if = "Option::is_none")]
123    #[builder(default, setter(into))]
124    pub(crate) domain: Option<Domain<'a>>,
125
126    /// The ID of the user. Required if you do not specify the user name.
127    #[serde(skip_serializing_if = "Option::is_none")]
128    #[builder(default, setter(into))]
129    pub(crate) id: Option<Cow<'a, str>>,
130
131    /// The user name. Required if you do not specify the ID of the user. If
132    /// you specify the user name, you must also specify the domain, by ID or
133    /// name.
134    #[serde(skip_serializing_if = "Option::is_none")]
135    #[builder(default, setter(into))]
136    pub(crate) name: Option<Cow<'a, str>>,
137
138    /// User Password
139    #[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/// The `password` object, contains the authentication information.
148#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
149#[builder(setter(strip_option))]
150pub struct Password<'a> {
151    /// A `user` object.
152    #[serde(skip_serializing_if = "Option::is_none")]
153    #[builder(default, setter(into))]
154    pub(crate) user: Option<PasswordUser<'a>>,
155}
156
157/// A `token` object
158#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
159#[builder(setter(strip_option))]
160pub struct Token {
161    /// Authorization Token value
162    #[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    /// A `domain` object
171    #[serde(skip_serializing_if = "Option::is_none")]
172    #[builder(default, setter(into))]
173    pub(crate) domain: Option<Domain<'a>>,
174
175    /// The user ID
176    #[serde(skip_serializing_if = "Option::is_none")]
177    #[builder(default, setter(into))]
178    pub(crate) id: Option<Cow<'a, str>>,
179
180    /// The user name
181    #[serde(skip_serializing_if = "Option::is_none")]
182    #[builder(default, setter(into))]
183    pub(crate) name: Option<Cow<'a, str>>,
184
185    /// MFA passcode
186    #[serde(serialize_with = "serialize_sensitive_string")]
187    #[builder(setter(into))]
188    pub(crate) passcode: SecretString,
189}
190
191/// Multi Factor Authentication information
192#[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/// An `identity` object.
201#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
202#[builder(setter(strip_option))]
203pub struct Identity<'a> {
204    /// An application credential object.
205    #[serde(skip_serializing_if = "Option::is_none")]
206    #[builder(default, setter(into))]
207    pub(crate) application_credential: Option<ApplicationCredential<'a>>,
208
209    /// The authentication method. For password authentication, specify
210    /// `password`.
211    #[serde()]
212    #[builder(setter(into))]
213    pub(crate) methods: Vec<Methods>,
214
215    /// The `password` object, contains the authentication information.
216    #[serde(skip_serializing_if = "Option::is_none")]
217    #[builder(default, setter(into))]
218    pub(crate) password: Option<Password<'a>>,
219
220    /// A `token` object
221    #[serde(skip_serializing_if = "Option::is_none")]
222    #[builder(default, setter(into))]
223    pub(crate) token: Option<Token>,
224
225    /// Multi Factor Authentication information
226    #[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    /// Domain id
243    #[serde(skip_serializing_if = "Option::is_none")]
244    #[builder(default, setter(into))]
245    pub(crate) id: Option<Cow<'a, str>>,
246
247    /// Domain name
248    #[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    /// Project domain Id
257    #[serde(skip_serializing_if = "Option::is_none")]
258    #[builder(default, setter(into))]
259    pub(crate) id: Option<Cow<'a, str>>,
260
261    /// Project domain Name
262    #[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    /// Project Id
275    #[serde(skip_serializing_if = "Option::is_none")]
276    #[builder(default, setter(into))]
277    pub(crate) id: Option<Cow<'a, str>>,
278
279    /// Project Name
280    #[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/// The authorization scope, including the system (Since v3.10), a project, or
294/// a domain (Since v3.4). If multiple scopes are specified in the same request
295/// (e.g. project and domain or domain and system) an HTTP 400 Bad Request will
296/// be returned, as a token cannot be simultaneously scoped to multiple
297/// authorization targets. An ID is sufficient to uniquely identify a project
298/// but if a project is specified by name, then the domain of the project must
299/// also be specified in order to uniquely identify the project by name. A
300/// domain scope may be specified by either the domain’s ID or name with
301/// equivalent results.
302#[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/// An `auth` object.
323#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
324#[builder(setter(strip_option))]
325pub struct Auth<'a> {
326    /// An `identity` object.
327    #[serde()]
328    #[builder(setter(into))]
329    pub(crate) identity: Identity<'a>,
330
331    /// The authorization scope, including the system (Since v3.10), a project,
332    /// or a domain (Since v3.4). If multiple scopes are specified in the same
333    /// request (e.g. project and domain or domain and system) an HTTP 400 Bad
334    /// Request will be returned, as a token cannot be simultaneously scoped to
335    /// multiple authorization targets. An ID is sufficient to uniquely
336    /// identify a project but if a project is specified by name, then the
337    /// domain of the project must also be specified in order to uniquely
338    /// identify the project by name. A domain scope may be specified by either
339    /// the domain’s ID or name with equivalent results.
340    #[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    /// An `auth` object.
349    #[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    /// Create a builder for the endpoint.
357    pub fn builder() -> RequestBuilder<'a> {
358        RequestBuilder::default()
359    }
360}
361
362impl<'a> RequestBuilder<'a> {
363    /// Add a single header to the Token.
364    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    /// Add multiple headers.
377    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    /// Returns headers to be set into the request
420    fn request_headers(&self) -> Option<&HeaderMap> {
421        self._headers.as_ref()
422    }
423
424    /// Returns required API version
425    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}