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    #[doc(hidden)]
86    #[allow(clippy::exhaustive_enums)]
87    pub enum ResponseBody {
88        Redirect,
89        Html(HtmlPage),
90    }
91
92    #[cfg(feature = "server")]
93    impl ruma_common::api::OutgoingBody for ResponseBody {
94        type Error = ruma_common::api::error::IntoHttpError;
95
96        fn content_type(&self) -> Option<http::HeaderValue> {
97            match self {
98                Self::Redirect => None,
99                Self::Html(_) => Some(ruma_common::http_headers::TEXT_HTML_UTF8),
100            }
101        }
102
103        fn try_into_buf<T: Default + bytes::BufMut + AsRef<[u8]>>(self) -> Result<T, Self::Error> {
104            let body = match self {
105                Self::Redirect => Vec::new(),
106                Self::Html(HtmlPage { body }) => body,
107            };
108
109            Ok(ruma_common::api::BytesBody(body).try_into_buf()?)
110        }
111    }
112
113    #[cfg(feature = "server")]
114    impl ruma_common::api::OutgoingResponse for Response {
115        type Body = ResponseBody;
116
117        fn try_into_http_response_inner(
118            self,
119        ) -> Result<http::Response<Self::Body>, ruma_common::api::error::IntoHttpError> {
120            match self {
121                Response::Redirect(Redirect { url }) => Ok(http::Response::builder()
122                    .status(http::StatusCode::FOUND)
123                    .header(http::header::LOCATION, url)
124                    .body(ResponseBody::Redirect)?),
125                Response::Html(html) => Ok(http::Response::builder()
126                    .status(http::StatusCode::OK)
127                    .body(ResponseBody::Html(html))?),
128            }
129        }
130    }
131
132    #[cfg(feature = "client")]
133    impl ruma_common::api::IncomingResponse for Response {
134        type EndpointError = ruma_common::api::error::Error;
135
136        fn try_from_http_response_inner(
137            response: http::Response<&[u8]>,
138        ) -> Result<Self, ruma_common::api::error::DeserializationError> {
139            use ruma_common::api::error::HeaderDeserializationError;
140
141            if response.status() == http::StatusCode::FOUND {
142                let Some(location) = response.headers().get(http::header::LOCATION) else {
143                    return Err(HeaderDeserializationError::MissingHeader(
144                        http::header::LOCATION.to_string(),
145                    )
146                    .into());
147                };
148
149                let url = location.to_str()?;
150                return Ok(Self::Redirect(Redirect { url: url.to_owned() }));
151            }
152
153            let body = response.into_body().to_owned();
154            Ok(Self::Html(HtmlPage { body }))
155        }
156    }
157
158    #[cfg(all(test, feature = "client"))]
159    mod tests_client {
160        use assert_matches2::assert_let;
161        use http::header::{CONTENT_TYPE, LOCATION};
162        use ruma_common::api::IncomingResponseExt as _;
163
164        use super::Response;
165
166        #[test]
167        fn incoming_redirect() {
168            use super::Redirect;
169
170            let http_response = http::Response::builder()
171                .status(http::StatusCode::FOUND)
172                .header(LOCATION, "http://localhost/redirect")
173                .body(b"".as_slice())
174                .unwrap();
175
176            let response = Response::try_from_http_response(http_response).unwrap();
177            assert_let!(Response::Redirect(Redirect { url }) = response);
178            assert_eq!(url, "http://localhost/redirect");
179        }
180
181        #[test]
182        fn incoming_html() {
183            use super::HtmlPage;
184
185            let http_response = http::Response::builder()
186                .status(http::StatusCode::OK)
187                .header(CONTENT_TYPE, ruma_common::http_headers::TEXT_HTML_UTF8)
188                .body(b"<h1>My Page</h1>".as_slice())
189                .unwrap();
190
191            let response = Response::try_from_http_response(http_response).unwrap();
192            assert_let!(Response::Html(HtmlPage { body }) = response);
193            assert_eq!(body, b"<h1>My Page</h1>");
194        }
195    }
196
197    #[cfg(all(test, feature = "server"))]
198    mod tests_server {
199        use http::header::{CONTENT_TYPE, LOCATION};
200        use ruma_common::api::OutgoingResponseExt as _;
201
202        use super::Response;
203
204        #[test]
205        fn outgoing_redirect() {
206            let response = Response::redirect("http://localhost/redirect".to_owned());
207
208            let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
209
210            assert_eq!(http_response.status(), http::StatusCode::FOUND);
211            assert_eq!(
212                http_response.headers().get(LOCATION).unwrap().to_str().unwrap(),
213                "http://localhost/redirect"
214            );
215            assert!(http_response.into_body().is_empty());
216        }
217
218        #[test]
219        fn outgoing_html() {
220            let response = Response::html(b"<h1>My Page</h1>".to_vec());
221
222            let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
223
224            assert_eq!(http_response.status(), http::StatusCode::OK);
225            assert_eq!(
226                http_response.headers().get(CONTENT_TYPE).unwrap(),
227                ruma_common::http_headers::TEXT_HTML_UTF8
228            );
229            assert_eq!(http_response.into_body(), b"<h1>My Page</h1>");
230        }
231    }
232}