oxide_auth_iron/
lib.rs

1//! Offers bindings for the code_grant module with iron servers.
2//!
3//! ## Hello world
4//!
5#![warn(missing_docs)]
6
7use std::borrow::Cow;
8
9use oxide_auth::endpoint::{OAuthError as EndpointError, QueryParameter, WebRequest, WebResponse};
10use oxide_auth::frontends::simple::endpoint::Error as SimpleError;
11
12use iron::{Request, Response};
13use iron::error::IronError;
14use iron::headers;
15use iron::status::Status;
16use url::Url;
17
18/// Errors while decoding requests.
19pub enum Error {
20    /// Generally describes a malformed request.
21    BadRequest,
22}
23
24#[derive(Debug)]
25/// Request type that can be derived from an `iron::Request`.
26///
27/// For now this is a shim around an `&mut iron::Request`, but in the future the implementation
28/// could change.
29pub struct OAuthRequest<'a, 'b, 'c: 'b>(&'a mut Request<'b, 'c>);
30
31#[derive(Debug)]
32/// Response type that can be coerced into an `iron::Response`.
33///
34/// For now this is a shim around an `iron::Response`, but in the future the implementation
35/// could change.
36pub struct OAuthResponse(Response);
37
38#[derive(Debug)]
39/// Generic error type produced by Oxide Auth operations that can be coerced into an `IronError`
40pub struct OAuthError(IronError);
41
42impl<'a, 'b, 'c: 'b> OAuthRequest<'a, 'b, 'c> {
43    /// Coerce an `iron::Request` into an OAuthRequest.
44    pub fn from_request(request: &'a mut Request<'b, 'c>) -> Self {
45        OAuthRequest(request)
46    }
47
48    /// Fetch the URL accessed for this request
49    pub fn url(&self) -> &iron::url::Url {
50        self.0.url.as_ref()
51    }
52
53    /// Fetch the query string from the request, returning an empty string if none was present.
54    pub fn query_string(&self) -> &str {
55        self.0.url.query().unwrap_or("")
56    }
57
58    /// Returns whether the request was sent with the correct ContentType header.
59    pub fn is_form_url_encoded(&self) -> bool {
60        self.0
61            .headers
62            .get::<headers::ContentType>()
63            .map(|ct| ct == &headers::ContentType::form_url_encoded())
64            .unwrap_or(false)
65    }
66
67    /// Fetch authorization header
68    pub fn authorization_header(&self) -> Option<Cow<str>> {
69        // Get the raw header.
70        self.0
71            .headers
72            .get::<headers::Authorization<String>>()
73            .map(|h| Cow::Borrowed(h.0.as_ref()))
74    }
75}
76
77impl OAuthResponse {
78    /// Create a new, empty OAuthResponse
79    pub fn new() -> Self {
80        OAuthResponse(Response::new())
81    }
82
83    /// Createa a new OAuthResponse from an existing `iron::Response`
84    pub fn from_response(response: Response) -> Self {
85        OAuthResponse(response)
86    }
87
88    /// Set the HTTP Status for the OAuthResponse
89    pub fn set_status(&mut self, status: Status) {
90        self.0.status = Some(status);
91    }
92
93    /// Set a header on the OAuthResponse
94    pub fn set_header<H>(&mut self, header: H)
95    where
96        H: headers::HeaderFormat + headers::Header,
97    {
98        self.0.headers.set(header);
99    }
100
101    /// Set a header on the OAuthResponse via name and value directly
102    pub fn set_raw_header(&mut self, name: Cow<'static, str>, values: Vec<Vec<u8>>) {
103        self.0.headers.set_raw(name, values);
104    }
105
106    /// Set the body on the OAuthResponse to the provided string
107    pub fn set_body(&mut self, body: &str) {
108        self.0.body = Some(Box::new(body.to_string()));
109    }
110}
111
112/// Requests are handed as mutable reference to the underlying object.
113impl<'a, 'b, 'c: 'b> WebRequest for OAuthRequest<'a, 'b, 'c> {
114    type Response = OAuthResponse;
115    type Error = Error;
116
117    fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
118        serde_urlencoded::from_str(self.query_string())
119            .map_err(|_| Error::BadRequest)
120            .map(Cow::Owned)
121    }
122
123    fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
124        let formatted = self.is_form_url_encoded();
125        if !formatted {
126            return Err(Error::BadRequest);
127        }
128
129        serde_urlencoded::from_reader(&mut self.0.body)
130            .map_err(|_| Error::BadRequest)
131            .map(Cow::Owned)
132    }
133
134    fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error> {
135        Ok(self.authorization_header())
136    }
137}
138
139impl WebResponse for OAuthResponse {
140    type Error = Error;
141
142    fn ok(&mut self) -> Result<(), Self::Error> {
143        self.set_status(Status::Ok);
144        Ok(())
145    }
146
147    fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
148        self.set_status(Status::Found);
149        self.set_header(headers::Location(url.into()));
150        Ok(())
151    }
152
153    fn client_error(&mut self) -> Result<(), Self::Error> {
154        self.set_status(Status::BadRequest);
155        Ok(())
156    }
157
158    fn unauthorized(&mut self, header_value: &str) -> Result<(), Self::Error> {
159        self.set_status(Status::Unauthorized);
160        let value_owned = header_value.as_bytes().to_vec();
161        self.set_raw_header("WWW-Authenticate".into(), vec![value_owned]);
162        Ok(())
163    }
164
165    fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
166        self.set_header(headers::ContentType::plaintext());
167        self.set_body(text);
168        Ok(())
169    }
170
171    fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
172        self.set_header(headers::ContentType::json());
173        self.set_body(data);
174        Ok(())
175    }
176}
177
178impl<'a, 'b, 'c: 'b> From<&'a mut Request<'b, 'c>> for OAuthRequest<'a, 'b, 'c> {
179    fn from(r: &'a mut Request<'b, 'c>) -> Self {
180        OAuthRequest::from_request(r)
181    }
182}
183
184impl<'a, 'b, 'c: 'b> Into<&'a mut Request<'b, 'c>> for OAuthRequest<'a, 'b, 'c> {
185    fn into(self) -> &'a mut Request<'b, 'c> {
186        self.0
187    }
188}
189
190impl From<Response> for OAuthResponse {
191    fn from(r: Response) -> Self {
192        OAuthResponse::from_response(r)
193    }
194}
195
196impl Into<Response> for OAuthResponse {
197    fn into(self) -> Response {
198        self.0
199    }
200}
201
202impl<'a, 'b, 'c: 'b> From<SimpleError<OAuthRequest<'a, 'b, 'c>>> for OAuthError {
203    fn from(error: SimpleError<OAuthRequest<'a, 'b, 'c>>) -> Self {
204        let as_oauth = match error {
205            SimpleError::Web(Error::BadRequest) => EndpointError::BadRequest,
206            SimpleError::OAuth(oauth) => oauth,
207        };
208
209        let status = match as_oauth {
210            EndpointError::BadRequest => Status::BadRequest,
211            EndpointError::DenySilently => Status::BadRequest,
212            EndpointError::PrimitiveError => Status::InternalServerError,
213        };
214
215        OAuthError(IronError::new(as_oauth, status))
216    }
217}
218
219impl From<IronError> for OAuthError {
220    fn from(e: IronError) -> Self {
221        OAuthError(e)
222    }
223}
224
225impl Into<IronError> for OAuthError {
226    fn into(self) -> IronError {
227        self.0
228    }
229}