#![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;
pub enum Error {
BadRequest,
}
#[derive(Debug)]
pub struct OAuthRequest<'a, 'b, 'c: 'b>(&'a mut Request<'b, 'c>);
#[derive(Debug)]
pub struct OAuthResponse(Response);
#[derive(Debug)]
pub struct OAuthError(IronError);
impl<'a, 'b, 'c: 'b> OAuthRequest<'a, 'b, 'c> {
pub fn from_request(request: &'a mut Request<'b, 'c>) -> Self {
OAuthRequest(request)
}
pub fn url(&self) -> &iron::url::Url {
self.0.url.as_ref()
}
pub fn query_string(&self) -> &str {
self.0.url.query().unwrap_or("")
}
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)
}
pub fn authorization_header(&self) -> Option<Cow<str>> {
self.0
.headers
.get::<headers::Authorization<String>>()
.map(|h| Cow::Borrowed(h.0.as_ref()))
}
}
impl OAuthResponse {
pub fn new() -> Self {
OAuthResponse(Response::new())
}
pub fn from_response(response: Response) -> Self {
OAuthResponse(response)
}
pub fn set_status(&mut self, status: Status) {
self.0.status = Some(status);
}
pub fn set_header<H>(&mut self, header: H)
where
H: headers::HeaderFormat + headers::Header,
{
self.0.headers.set(header);
}
pub fn set_raw_header(&mut self, name: Cow<'static, str>, values: Vec<Vec<u8>>) {
self.0.headers.set_raw(name, values);
}
pub fn set_body(&mut self, body: &str) {
self.0.body = Some(Box::new(body.to_string()));
}
}
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()));
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
}
}