1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//! Offers bindings for the code_grant module with iron servers.
//!
//! ## Hello world
//!
#![warn(missing_docs)]

use std::borrow::Cow;

use oxide_auth::endpoint::{OAuthError as EndpointError, QueryParameter, WebRequest, WebResponse};
use oxide_auth::frontends::simple::endpoint::Error as SimpleError;

use iron::{Request, Response};
use iron::error::IronError;
use iron::headers;
use iron::status::Status;
use url::Url;

/// Errors while decoding requests.
pub enum Error {
    /// Generally describes a malformed request.
    BadRequest,
}

#[derive(Debug)]
/// Request type that can be derived from an `iron::Request`.
///
/// For now this is a shim around an `&mut iron::Request`, but in the future the implementation
/// could change.
pub struct OAuthRequest<'a, 'b, 'c: 'b>(&'a mut Request<'b, 'c>);

#[derive(Debug)]
/// Response type that can be coerced into an `iron::Response`.
///
/// For now this is a shim around an `iron::Response`, but in the future the implementation
/// could change.
pub struct OAuthResponse(Response);

#[derive(Debug)]
/// Generic error type produced by Oxide Auth operations that can be coerced into an `IronError`
pub struct OAuthError(IronError);

impl<'a, 'b, 'c: 'b> OAuthRequest<'a, 'b, 'c> {
    /// Coerce an `iron::Request` into an OAuthRequest.
    pub fn from_request(request: &'a mut Request<'b, 'c>) -> Self {
        OAuthRequest(request)
    }

    /// Fetch the URL accessed for this request
    pub fn url(&self) -> &iron::url::Url {
        self.0.url.as_ref()
    }

    /// Fetch the query string from the request, returning an empty string if none was present.
    pub fn query_string(&self) -> &str {
        self.0.url.query().unwrap_or("")
    }

    /// Returns whether the request was sent with the correct ContentType header.
    pub fn is_form_url_encoded(&self) -> bool {
        self.0
            .headers
            .get::<headers::ContentType>()
            .map(|ct| ct == &headers::ContentType::form_url_encoded())
            .unwrap_or(false)
    }

    /// Fetch authorization header
    pub fn authorization_header(&self) -> Option<Cow<str>> {
        // Get the raw header.
        self.0
            .headers
            .get::<headers::Authorization<String>>()
            .map(|h| Cow::Borrowed(h.0.as_ref()))
    }
}

impl OAuthResponse {
    /// Create a new, empty OAuthResponse
    pub fn new() -> Self {
        OAuthResponse(Response::new())
    }

    /// Createa a new OAuthResponse from an existing `iron::Response`
    pub fn from_response(response: Response) -> Self {
        OAuthResponse(response)
    }

    /// Set the HTTP Status for the OAuthResponse
    pub fn set_status(&mut self, status: Status) {
        self.0.status = Some(status);
    }

    /// Set a header on the OAuthResponse
    pub fn set_header<H>(&mut self, header: H)
    where
        H: headers::HeaderFormat + headers::Header,
    {
        self.0.headers.set(header);
    }

    /// Set a header on the OAuthResponse via name and value directly
    pub fn set_raw_header(&mut self, name: Cow<'static, str>, values: Vec<Vec<u8>>) {
        self.0.headers.set_raw(name, values);
    }

    /// Set the body on the OAuthResponse to the provided string
    pub fn set_body(&mut self, body: &str) {
        self.0.body = Some(Box::new(body.to_string()));
    }
}

/// Requests are handed as mutable reference to the underlying object.
impl<'a, 'b, 'c: 'b> WebRequest for OAuthRequest<'a, 'b, 'c> {
    type Response = OAuthResponse;
    type Error = Error;

    fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
        serde_urlencoded::from_str(self.query_string())
            .map_err(|_| Error::BadRequest)
            .map(Cow::Owned)
    }

    fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
        let formatted = self.is_form_url_encoded();
        if !formatted {
            return Err(Error::BadRequest);
        }

        serde_urlencoded::from_reader(&mut self.0.body)
            .map_err(|_| Error::BadRequest)
            .map(Cow::Owned)
    }

    fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error> {
        Ok(self.authorization_header())
    }
}

impl WebResponse for OAuthResponse {
    type Error = Error;

    fn ok(&mut self) -> Result<(), Self::Error> {
        self.set_status(Status::Ok);
        Ok(())
    }

    fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
        self.set_status(Status::Found);
        self.set_header(headers::Location(url.into_string()));
        Ok(())
    }

    fn client_error(&mut self) -> Result<(), Self::Error> {
        self.set_status(Status::BadRequest);
        Ok(())
    }

    fn unauthorized(&mut self, header_value: &str) -> Result<(), Self::Error> {
        self.set_status(Status::Unauthorized);
        let value_owned = header_value.as_bytes().to_vec();
        self.set_raw_header("WWW-Authenticate".into(), vec![value_owned]);
        Ok(())
    }

    fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
        self.set_header(headers::ContentType::plaintext());
        self.set_body(text);
        Ok(())
    }

    fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
        self.set_header(headers::ContentType::json());
        self.set_body(data);
        Ok(())
    }
}

impl<'a, 'b, 'c: 'b> From<&'a mut Request<'b, 'c>> for OAuthRequest<'a, 'b, 'c> {
    fn from(r: &'a mut Request<'b, 'c>) -> Self {
        OAuthRequest::from_request(r)
    }
}

impl<'a, 'b, 'c: 'b> Into<&'a mut Request<'b, 'c>> for OAuthRequest<'a, 'b, 'c> {
    fn into(self) -> &'a mut Request<'b, 'c> {
        self.0
    }
}

impl From<Response> for OAuthResponse {
    fn from(r: Response) -> Self {
        OAuthResponse::from_response(r)
    }
}

impl Into<Response> for OAuthResponse {
    fn into(self) -> Response {
        self.0
    }
}

impl<'a, 'b, 'c: 'b> From<SimpleError<OAuthRequest<'a, 'b, 'c>>> for OAuthError {
    fn from(error: SimpleError<OAuthRequest<'a, 'b, 'c>>) -> Self {
        let as_oauth = match error {
            SimpleError::Web(Error::BadRequest) => EndpointError::BadRequest,
            SimpleError::OAuth(oauth) => oauth,
        };

        let status = match as_oauth {
            EndpointError::BadRequest => Status::BadRequest,
            EndpointError::DenySilently => Status::BadRequest,
            EndpointError::PrimitiveError => Status::InternalServerError,
        };

        OAuthError(IronError::new(as_oauth, status))
    }
}

impl From<IronError> for OAuthError {
    fn from(e: IronError) -> Self {
        OAuthError(e)
    }
}

impl Into<IronError> for OAuthError {
    fn into(self) -> IronError {
        self.0
    }
}