oxide_auth_rouille/
lib.rs1#![warn(missing_docs)]
6
7use core::ops::Deref;
8use std::borrow::Cow;
9
10use oxide_auth::endpoint::{QueryParameter, WebRequest, WebResponse};
11
12use rouille;
13use url::Url;
14
15pub use oxide_auth::frontends::simple::endpoint::{FnSolicitor, Generic as GenericEndpoint, Vacant};
18
19#[derive(Debug)]
21pub enum WebError {
22 Encoding,
27}
28
29#[derive(Debug)]
30pub struct Request<'a> {
32 inner: &'a rouille::Request,
33}
34
35#[derive(Debug)]
36pub struct Response {
38 inner: rouille::Response,
39}
40
41impl<'a> Request<'a> {
42 pub fn new(inner: &'a rouille::Request) -> Self {
44 Request { inner }
45 }
46}
47
48impl Response {
49 pub fn into_inner(self) -> rouille::Response {
51 self.inner
52 }
53}
54
55impl From<rouille::Response> for Response {
56 fn from(inner: rouille::Response) -> Self {
57 Response { inner }
58 }
59}
60
61impl From<Response> for rouille::Response {
62 fn from(response: Response) -> Self {
63 response.inner
64 }
65}
66
67impl<'a> WebRequest for Request<'a> {
68 type Error = WebError;
69 type Response = Response;
70
71 fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
72 let query = self.inner.raw_query_string();
73 let data = serde_urlencoded::from_str(query).map_err(|_| WebError::Encoding)?;
74 Ok(Cow::Owned(data))
75 }
76
77 fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
78 match self.inner.header("Content-Type") {
79 None | Some("application/x-www-form-urlencoded") => (),
80 _ => return Err(WebError::Encoding),
81 }
82
83 let body = self.inner.data().ok_or(WebError::Encoding)?;
84 let data = serde_urlencoded::from_reader(body).map_err(|_| WebError::Encoding)?;
85 Ok(Cow::Owned(data))
86 }
87
88 fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error> {
89 Ok(self.inner.header("Authorization").map(|st| st.into()))
90 }
91}
92
93impl WebResponse for Response {
94 type Error = WebError;
95
96 fn ok(&mut self) -> Result<(), Self::Error> {
97 self.inner.status_code = 200;
98 Ok(())
99 }
100
101 fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
102 self.inner.status_code = 302;
103 self.inner
104 .headers
105 .retain(|header| !header.0.eq_ignore_ascii_case("Location"));
106 self.inner
107 .headers
108 .push(("Location".into(), String::from(url).into()));
109 Ok(())
110 }
111
112 fn client_error(&mut self) -> Result<(), Self::Error> {
113 self.inner.status_code = 400;
114 Ok(())
115 }
116
117 fn unauthorized(&mut self, kind: &str) -> Result<(), Self::Error> {
118 self.inner.status_code = 401;
119 self.inner
120 .headers
121 .retain(|header| !header.0.eq_ignore_ascii_case("www-authenticate"));
122 self.inner
123 .headers
124 .push(("WWW-Authenticate".into(), kind.to_string().into()));
125 Ok(())
126 }
127
128 fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
129 self.inner
130 .headers
131 .retain(|header| !header.0.eq_ignore_ascii_case("Content-Type"));
132 self.inner
133 .headers
134 .push(("Content-Type".into(), "text/plain".into()));
135 self.inner.data = rouille::ResponseBody::from_string(text);
136 Ok(())
137 }
138
139 fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
140 self.inner
141 .headers
142 .retain(|header| !header.0.eq_ignore_ascii_case("Content-Type"));
143 self.inner
144 .headers
145 .push(("Content-Type".into(), "application/json".into()));
146 self.inner.data = rouille::ResponseBody::from_string(data);
147 Ok(())
148 }
149}
150
151impl Deref for Request<'_> {
152 type Target = rouille::Request;
153
154 fn deref(&self) -> &Self::Target {
155 self.inner
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn multi_query() {
165 let request =
166 &rouille::Request::fake_http("GET", "/authorize?fine=val¶m=a¶m=b", vec![], vec![]);
167 let mut request = Request::new(request);
168 let query = WebRequest::query(&mut request).unwrap();
169
170 assert_eq!(Some(Cow::Borrowed("val")), query.unique_value("fine"));
171 assert_eq!(None, query.unique_value("param"));
172 }
173}