Skip to main content

ruma_client_api/uiaa/
get_uiaa_fallback_page.rs

1//! `GET /_matrix/client/*/auth/{auth_type}/fallback/web?session={session_id}`
2//!
3//! Get UIAA fallback web page.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/v1.19/client-server-api/#fallback
9
10    use ruma_common::{
11        api::{auth_scheme::NoAccessToken, request},
12        metadata,
13    };
14
15    use crate::uiaa::AuthType;
16
17    metadata! {
18        method: GET,
19        rate_limited: false,
20        authentication: NoAccessToken,
21        history: {
22            1.0 => "/_matrix/client/r0/auth/{auth_type}/fallback/web",
23            1.1 => "/_matrix/client/v3/auth/{auth_type}/fallback/web",
24        }
25    }
26
27    /// Request type for the `authorize_fallback` endpoint.
28    #[request]
29    pub struct Request {
30        /// The type name (`m.login.dummy`, etc.) of the UIAA stage to get a fallback page for.
31        #[ruma_api(path)]
32        pub auth_type: AuthType,
33
34        /// The ID of the session given by the homeserver.
35        #[ruma_api(query)]
36        pub session: String,
37    }
38
39    impl Request {
40        /// Creates a new `Request` with the given auth type and session ID.
41        pub fn new(auth_type: AuthType, session: String) -> Self {
42            Self { auth_type, session }
43        }
44    }
45
46    /// Response type for the `authorize_fallback` endpoint.
47    #[derive(Debug, Clone)]
48    #[allow(clippy::exhaustive_enums)]
49    pub enum Response {
50        /// The response is a redirect.
51        Redirect(Redirect),
52
53        /// The response is an HTML page.
54        Html(HtmlPage),
55    }
56
57    /// The data of a redirect.
58    #[derive(Debug, Clone)]
59    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
60    pub struct Redirect {
61        /// The URL to redirect the user to.
62        pub url: String,
63    }
64
65    /// The data of a HTML page.
66    #[derive(Debug, Clone)]
67    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
68    pub struct HtmlPage {
69        /// The body of the HTML page.
70        pub body: Vec<u8>,
71    }
72
73    impl Response {
74        /// Creates a new HTML `Response` with the given HTML body.
75        pub fn html(body: Vec<u8>) -> Self {
76            Self::Html(HtmlPage { body })
77        }
78
79        /// Creates a new HTML `Response` with the given redirect URL.
80        pub fn redirect(url: String) -> Self {
81            Self::Redirect(Redirect { url })
82        }
83    }
84
85    #[cfg(feature = "server")]
86    impl ruma_common::api::OutgoingResponse for Response {
87        fn try_into_http_response<T: Default + bytes::BufMut>(
88            self,
89        ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
90            match self {
91                Response::Redirect(Redirect { url }) => Ok(http::Response::builder()
92                    .status(http::StatusCode::FOUND)
93                    .header(http::header::LOCATION, url)
94                    .body(T::default())?),
95                Response::Html(HtmlPage { body }) => Ok(http::Response::builder()
96                    .status(http::StatusCode::OK)
97                    .header(http::header::CONTENT_TYPE, "text/html; charset=utf-8")
98                    .body(ruma_common::serde::slice_to_buf(&body))?),
99            }
100        }
101    }
102
103    #[cfg(feature = "client")]
104    impl ruma_common::api::IncomingResponse for Response {
105        type EndpointError = ruma_common::api::error::Error;
106
107        fn try_from_http_response_inner(
108            response: http::Response<&[u8]>,
109        ) -> Result<Self, ruma_common::api::error::DeserializationError> {
110            use ruma_common::api::error::HeaderDeserializationError;
111
112            if response.status() == http::StatusCode::FOUND {
113                let Some(location) = response.headers().get(http::header::LOCATION) else {
114                    return Err(HeaderDeserializationError::MissingHeader(
115                        http::header::LOCATION.to_string(),
116                    )
117                    .into());
118                };
119
120                let url = location.to_str()?;
121                return Ok(Self::Redirect(Redirect { url: url.to_owned() }));
122            }
123
124            let body = response.into_body().to_owned();
125            Ok(Self::Html(HtmlPage { body }))
126        }
127    }
128
129    #[cfg(all(test, feature = "client"))]
130    mod tests_client {
131        use assert_matches2::assert_let;
132        use http::header::{CONTENT_TYPE, LOCATION};
133        use ruma_common::api::IncomingResponseExt as _;
134
135        use super::Response;
136
137        #[test]
138        fn incoming_redirect() {
139            use super::Redirect;
140
141            let http_response = http::Response::builder()
142                .status(http::StatusCode::FOUND)
143                .header(LOCATION, "http://localhost/redirect")
144                .body(b"".as_slice())
145                .unwrap();
146
147            let response = Response::try_from_http_response(http_response).unwrap();
148            assert_let!(Response::Redirect(Redirect { url }) = response);
149            assert_eq!(url, "http://localhost/redirect");
150        }
151
152        #[test]
153        fn incoming_html() {
154            use super::HtmlPage;
155
156            let http_response = http::Response::builder()
157                .status(http::StatusCode::OK)
158                .header(CONTENT_TYPE, "text/html; charset=utf-8")
159                .body(b"<h1>My Page</h1>".as_slice())
160                .unwrap();
161
162            let response = Response::try_from_http_response(http_response).unwrap();
163            assert_let!(Response::Html(HtmlPage { body }) = response);
164            assert_eq!(body, b"<h1>My Page</h1>");
165        }
166    }
167
168    #[cfg(all(test, feature = "server"))]
169    mod tests_server {
170        use http::header::{CONTENT_TYPE, LOCATION};
171        use ruma_common::api::OutgoingResponse;
172
173        use super::Response;
174
175        #[test]
176        fn outgoing_redirect() {
177            let response = Response::redirect("http://localhost/redirect".to_owned());
178
179            let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
180
181            assert_eq!(http_response.status(), http::StatusCode::FOUND);
182            assert_eq!(
183                http_response.headers().get(LOCATION).unwrap().to_str().unwrap(),
184                "http://localhost/redirect"
185            );
186            assert!(http_response.into_body().is_empty());
187        }
188
189        #[test]
190        fn outgoing_html() {
191            let response = Response::html(b"<h1>My Page</h1>".to_vec());
192
193            let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
194
195            assert_eq!(http_response.status(), http::StatusCode::OK);
196            assert_eq!(
197                http_response.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(),
198                "text/html; charset=utf-8"
199            );
200            assert_eq!(http_response.into_body(), b"<h1>My Page</h1>");
201        }
202    }
203}