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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//! OAuth requests encapsulated as futures.
//!
//! Some requests are dependent on data inside the request body, which is loaded asynchronously
//! by actix.  In order to provide a uniform interface, all requests are encapsulated into a
//! future yielding the specific message to be sent to the endpoint.
use std::borrow::Cow;

use endpoint::{OAuthError, NormalizedParameter, PreGrant, QueryParameter, WebRequest, WebResponse};
use frontends::simple::request::{Body, Response, Status};

use super::actix_web::{HttpMessage, HttpRequest, HttpResponse};
use super::actix_web::dev::UrlEncoded;
use super::actix_web::http::header::{self, HeaderValue};
use super::futures::{Async, Future, Poll};
use super::message::{AuthorizationCode, AccessToken, Resource};

use url::Url;
use super::serde_urlencoded;

/// A future for all OAuth related data.
pub struct OAuthFuture {
    inner: HttpRequest,
    body: Option<UrlEncoded<HttpRequest, NormalizedParameter>>,
}

/// Sendable struct implementing `WebRequest`.
#[derive(Clone, Debug)]
pub struct OAuthRequest {
    query: Result<NormalizedParameter, ()>,
    auth: Result<Option<String>, ()>,
    // None if not urlencoded body or error in encoding
    body: Result<NormalizedParameter, ()>,
}

/// An http response replacement that can be sent as an actix message.
///
/// This is the generic answer to oauth authorization code and bearer token requests.
#[derive(Clone, Debug, Default)]
pub struct OAuthResponse {
    inner: ResponseKind,
}

#[derive(Clone, Debug)]
enum ResponseKind {
    ConsentForm(PreGrant),
    Inner(Response),
}

impl OAuthFuture {
    /// Extract relevant components from a request.
    ///
    /// The result of the future is `Send + Sync` so that it can be used in messages.
    pub fn new<S>(request: &HttpRequest<S>) -> Self {
        let request = request.drop_state();
        let body = if let Some(ctype) = request.request().headers().get(header::CONTENT_TYPE) {
            if ctype == "application/x-www-form-urlencoded" {
                Some(UrlEncoded::new(&request))
            } else {
                None
            }
        } else {
            None
        };

        OAuthFuture {
            inner: request,
            body,
        }
    }
}

impl OAuthRequest {
    /// Utility method to turn this request into an actix message.
    ///
    /// The resulting message can be sent to an `Endpoint` actor to ask for an authorization code.
    pub fn authorization_code(self) -> AuthorizationCode {
        AuthorizationCode(self)
    }

    /// Utility method to turn this request into an actix message.
    ///
    /// The resulting message can be sent to an `Endpoint` actor to trade an authorization code
    /// again an access token.
    pub fn access_token(self) -> AccessToken {
        AccessToken(self)
    }

    /// Utility method to turn this request into an actix message.
    ///
    /// The resulting message can be sent to an `Endpoint` actor to assert the presented
    /// authorization token has appropriate permission to access some resource.
    pub fn resource(self) -> Resource {
        Resource(self)
    }
}

impl OAuthResponse {
    /// Construct a response that represents a consent form.
    ///
    /// Use this in an `OwnerSolicitor` implementation for `OAuthRequest` to construct the
    /// `OwnerConsent::InProgress` variant.
    pub fn consent_form(grant: PreGrant) -> Self {
        OAuthResponse {
            inner: ResponseKind::ConsentForm(grant),
        }
    }

    /// Create the response with predetermined content.
    pub fn new(inner: Response) -> Self {
        OAuthResponse {
            inner: ResponseKind::Inner(inner),
        }
    }

    /// Retrive the internal response, assuming that it does not require consent.
    pub fn unwrap(self) -> HttpResponse {
        match self.inner {
            ResponseKind::Inner(inner) => Self::convert(inner),
            ResponseKind::ConsentForm(_) => HttpResponse::InternalServerError().finish(),
        }
    }

    /// Convert the response into an http response.
    ///
    /// When the response represents a required consent form, use the argument as the response to
    /// the user agent instead. This replacement response should generally display the wanting
    /// client and required scopes in a clear and click-jacking protected manner to the
    /// authenticated resource owner. When the resource owner is not currently authenticated (i.e.
    /// logged in), this is a good opportunity to redirect to such a login page.
    pub fn get_or_consent(self, consent: HttpResponse) -> HttpResponse {
        match self.inner {
            ResponseKind::Inner(inner) => Self::convert(inner),
            ResponseKind::ConsentForm(_) => consent,
        }
    }

    /// Convert the response into an http response.
    ///
    /// When the response represents a required consent form, use the provided function to
    /// construct the response to the user agent instead. This replacement response should
    /// generally display the wanting client and required scopes in a clear and click-jacking
    /// protected manner to the authenticated resource owner. When the resource owner is not
    /// currently authenticated (i.e.  logged in), this is a good opportunity to redirect to such a
    /// login page.
    pub fn get_or_consent_with<F>(self, f: F) -> HttpResponse 
        where F: FnOnce(PreGrant) -> HttpResponse 
    {
        match self.inner {
            ResponseKind::Inner(inner) => Self::convert(inner),
            ResponseKind::ConsentForm(grant) => f(grant),
        }
    }

    fn convert(response: Response) -> HttpResponse {
        let mut builder = match response.status {
            Status::Ok => HttpResponse::Ok(),
            Status::Redirect => HttpResponse::Found(),
            Status::BadRequest => HttpResponse::BadRequest(),
            Status::Unauthorized => HttpResponse::Unauthorized(),
        };

        if let Some(url) = response.location {
            builder.header(header::LOCATION, url.into_string());
        }

        if let Some(auth) = &response.www_authenticate {
            builder.header(header::WWW_AUTHENTICATE, HeaderValue::from_str(auth).unwrap());
        }

        match response.body {
            Some(Body::Text(text)) => {
                builder.content_type("text/plain");
                builder.body(text)
            },
            Some(Body::Json(text)) => {
                builder.content_type("application/json");
                builder.body(text)
            },
            None => builder.finish() ,
        }
    }
}

impl ResponseKind {
    fn transform(&mut self) -> &mut Response {
        match self {
            ResponseKind::Inner(ref mut inner) => inner,
            // No consent form, this response is predetermined.
            ResponseKind::ConsentForm(_) => {
                *self = ResponseKind::Inner(Response::default());
                self.transform()
            },
        }
    }
}

impl Future for OAuthFuture {
    type Item = OAuthRequest;
    type Error = OAuthError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let body = match self.body.as_mut().map(Future::poll) {
            Some(Ok(Async::NotReady)) => return Ok(Async::NotReady),
            Some(Ok(Async::Ready(body))) => Ok(body),
            Some(Err(_)) => Err(()),
            None => Err(()),
        };

        // We can not trust actix not deduplicating keys.
        let query = match self.inner.uri().query().map(serde_urlencoded::from_str) {
            None => Ok(NormalizedParameter::default()),
            Some(Ok(query)) => Ok(query),
            Some(Err(_)) => Err(()),
        };

        let auth = self.inner.headers()
            .get(header::AUTHORIZATION)
            .map(|header| header.to_str().map(str::to_string));

        let auth = match auth {
            Some(Ok(auth)) => Ok(Some(auth)),
            Some(Err(_)) => Err(()),
            None => Ok(None),
        };

        Ok(Async::Ready(OAuthRequest {
            query,
            auth,
            body,
        }))
    }
}

impl Default for ResponseKind {
    fn default() -> Self {
        ResponseKind::Inner(Response::default())
    }
}

impl WebRequest for OAuthRequest {
    type Error = OAuthError;
    type Response = OAuthResponse;

     fn query(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
         self.query.as_ref()
             .map(|query| Cow::Borrowed(query as &dyn QueryParameter))
             .map_err(|_| OAuthError::BadRequest)
     }

     fn urlbody(&mut self) -> Result<Cow<dyn QueryParameter + 'static>, Self::Error> {
         self.body.as_ref()
             .map(|body| Cow::Borrowed(body as &dyn QueryParameter))
             .map_err(|_| OAuthError::BadRequest)
     }

     fn authheader(&mut self) -> Result<Option<Cow<str>>, Self::Error>{
         match &self.auth {
             Ok(Some(string)) => Ok(Some(Cow::Borrowed(string))),
             Ok(None) => Ok(None),
             Err(_) => Err(OAuthError::BadRequest)
         }
     }
}

impl WebResponse for OAuthResponse {
    type Error = OAuthError;

    fn ok(&mut self) -> Result<(), Self::Error> {
        self.inner.transform().ok().map_err(|err| match err {})
    }

    fn redirect(&mut self, url: Url) -> Result<(), Self::Error> {
        self.inner.transform().redirect(url).map_err(|err| match err {})
    }

    fn client_error(&mut self) -> Result<(), Self::Error> {
        self.inner.transform().client_error().map_err(|err| match err {})
    }

    fn unauthorized(&mut self, kind: &str) -> Result<(), Self::Error> {
        self.inner.transform().unauthorized(kind).map_err(|err| match err {})
    }

    fn body_text(&mut self, text: &str) -> Result<(), Self::Error> {
        self.inner.transform().body_text(text).map_err(|err| match err {})
    }

    fn body_json(&mut self, data: &str) -> Result<(), Self::Error> {
        self.inner.transform().body_json(data).map_err(|err| match err {})
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[allow(dead_code)]
    fn is_send_sync() {
        trait Test: Send + Sync + 'static { }
        impl Test for OAuthRequest { }
        impl Test for OAuthResponse { }
    }
}