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;
pub struct OAuthFuture {
inner: HttpRequest,
body: Option<UrlEncoded<HttpRequest, NormalizedParameter>>,
}
#[derive(Clone, Debug)]
pub struct OAuthRequest {
query: Result<NormalizedParameter, ()>,
auth: Result<Option<String>, ()>,
body: Result<NormalizedParameter, ()>,
}
#[derive(Clone, Debug, Default)]
pub struct OAuthResponse {
inner: ResponseKind,
}
#[derive(Clone, Debug)]
enum ResponseKind {
ConsentForm(PreGrant),
Inner(Response),
}
impl OAuthFuture {
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 {
pub fn authorization_code(self) -> AuthorizationCode {
AuthorizationCode(self)
}
pub fn access_token(self) -> AccessToken {
AccessToken(self)
}
pub fn resource(self) -> Resource {
Resource(self)
}
}
impl OAuthResponse {
pub fn consent_form(grant: PreGrant) -> Self {
OAuthResponse {
inner: ResponseKind::ConsentForm(grant),
}
}
pub fn new(inner: Response) -> Self {
OAuthResponse {
inner: ResponseKind::Inner(inner),
}
}
pub fn unwrap(self) -> HttpResponse {
match self.inner {
ResponseKind::Inner(inner) => Self::convert(inner),
ResponseKind::ConsentForm(_) => HttpResponse::InternalServerError().finish(),
}
}
pub fn get_or_consent(self, consent: HttpResponse) -> HttpResponse {
match self.inner {
ResponseKind::Inner(inner) => Self::convert(inner),
ResponseKind::ConsentForm(_) => consent,
}
}
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,
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(()),
};
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 { }
}
}