extern crate iron;
extern crate urlencoded;
use code_grant::prelude::*;
use code_grant::frontend::{AccessFlow, AuthorizationFlow, GrantFlow, OwnerAuthorizer, WebRequest, WebResponse};
pub use code_grant::frontend::{Authentication, OAuthError};
pub use code_grant::prelude::{PreGrant, Scope};
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, LockResult, MutexGuard};
use std::ops::DerefMut;
use std::marker::PhantomData;
use self::iron::{BeforeMiddleware, Handler, IronResult, IronError, Plugin, Url as IronUrl};
use self::iron::headers::{Authorization as AuthHeader, ContentType};
use self::iron::modifiers::Header;
use self::iron::request::Request;
use self::iron::response::Response;
use self::iron::status;
use self::iron::typemap;
use self::iron::modifiers::Redirect;
use self::urlencoded::{UrlEncodedBody, UrlEncodedQuery};
use url::Url;
pub struct IronGranter<R, A, I> where
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static,
I: Issuer + Send + 'static
{
registrar: Arc<Mutex<R>>,
authorizer: Arc<Mutex<A>>,
issuer: Arc<Mutex<I>>,
}
pub struct IronAuthorizer<PH, R, A> where
PH: GenericOwnerAuthorizer + Send + Sync,
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static,
{
page_handler: Box<PH>,
registrar: Arc<Mutex<R>>,
authorizer: Arc<Mutex<A>>,
}
pub struct IronTokenRequest<R, A, I> where
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static,
I: Issuer + Send + 'static
{
registrar: Arc<Mutex<R>>,
authorizer: Arc<Mutex<A>>,
issuer: Arc<Mutex<I>>,
}
pub struct IronGuard<I> where
I: Issuer + Send + 'static
{
scopes: Vec<Scope>,
issuer: Arc<Mutex<I>>,
}
impl typemap::Key for PreGrant { type Value = PreGrant; }
impl typemap::Key for Authentication { type Value = Authentication; }
pub trait GenericOwnerAuthorizer: Send + Sync + 'static {
fn get_owner_authorization(&self, &mut Request, &PreGrant) -> IronResult<(Authentication, Response)>;
}
pub struct IronOwnerAuthorizer<A: Handler>(pub A);
impl GenericOwnerAuthorizer for Handler {
fn get_owner_authorization(&self, req: &mut Request, auth: &PreGrant)
-> IronResult<(Authentication, Response)> {
req.extensions.insert::<PreGrant>(auth.clone());
let response = self.handle(req)?;
match req.extensions.get::<Authentication>() {
None => return Ok((Authentication::Failed, Response::with((status::InternalServerError, "No authentication response")))),
Some(v) => return Ok((v.clone(), response)),
};
}
}
impl<F> GenericOwnerAuthorizer for F
where F :Fn(&mut Request, &PreGrant) -> Result<(Authentication, Response), OAuthError> + Send + Sync + 'static {
fn get_owner_authorization(&self, req: &mut Request, auth: &PreGrant)
-> IronResult<(Authentication, Response)> {
self(req, auth).map_err(|o| o.into())
}
}
impl<A: Handler> GenericOwnerAuthorizer for IronOwnerAuthorizer<A> {
fn get_owner_authorization(&self, req: &mut Request, auth: &PreGrant)
-> IronResult<(Authentication, Response)> {
(&self.0 as &Handler).get_owner_authorization(req, auth)
}
}
struct SpecificOwnerAuthorizer<'l, 'a, 'b: 'a>(&'l GenericOwnerAuthorizer, PhantomData<Request<'a, 'b>>);
impl<'l, 'a, 'b: 'a> OwnerAuthorizer for SpecificOwnerAuthorizer<'l, 'a, 'b> {
type Request = Request<'a, 'b>;
fn get_owner_authorization(&self, req: &mut Self::Request, auth: &PreGrant)
-> IronResult<(Authentication, Response)> {
self.0.get_owner_authorization(req, auth)
}
}
impl<'a, 'b> WebRequest for Request<'a, 'b> {
type Response = Response;
type Error = IronError;
fn query(&mut self) -> Result<Cow<HashMap<String, Vec<String>>>, ()> {
match self.get_ref::<UrlEncodedQuery>() {
Ok(query) => Ok(Cow::Borrowed(query)),
Err(_) => Err(()),
}
}
fn urlbody(&mut self) -> Result<Cow<HashMap<String, Vec<String>>>, ()> {
match self.get_ref::<UrlEncodedBody>() {
Ok(query) => Ok(Cow::Borrowed(query)),
Err(_) => Err(()),
}
}
fn authheader(&mut self) -> Result<Option<Cow<str>>, ()> {
match self.headers.get::<AuthHeader<String>>() {
None => Ok(None),
Some(hdr) => Ok(Some(Cow::Borrowed(&hdr))),
}
}
}
impl WebResponse for Response {
type Error = IronError;
fn redirect(url: Url) -> Result<Response, IronError> {
let real_url = match IronUrl::from_generic_url(url) {
Err(_) => return Err(IronError::new(OAuthError::InternalCodeError(),
status::InternalServerError)),
Ok(v) => v,
};
Ok(Response::with((status::Found, Redirect(real_url))))
}
fn text(text: &str) -> Result<Response, IronError> {
Ok(Response::with((status::Ok, text)))
}
fn json(data: &str) -> Result<Response, IronError> {
Ok(Response::with((
status::Ok,
Header(ContentType::json()),
data,
)))
}
fn as_client_error(mut self) -> Result<Self, IronError> {
self.status = Some(status::BadRequest);
Ok(self)
}
fn as_unauthorized(mut self) -> Result<Self, IronError> {
self.status = Some(status::Unauthorized);
Ok(self)
}
fn with_authorization(mut self, kind: &str) -> Result<Self, IronError> {
self.headers.set_raw("WWW-Authenticate", vec![kind.as_bytes().to_vec()]);
Ok(self)
}
}
impl<R, A, I> IronGranter<R, A, I> where
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static,
I: Issuer + Send + 'static
{
pub fn new(registrar: R, data: A, issuer: I) -> IronGranter<R, A, I> {
IronGranter {
registrar: Arc::new(Mutex::new(registrar)),
authorizer: Arc::new(Mutex::new(data)),
issuer: Arc::new(Mutex::new(issuer)) }
}
pub fn authorize<H: GenericOwnerAuthorizer + Send + Sync>(&self, page_handler: H) -> IronAuthorizer<H, R, A> {
IronAuthorizer {
authorizer: self.authorizer.clone(),
page_handler: Box::new(page_handler),
registrar: self.registrar.clone() }
}
pub fn token(&self) -> IronTokenRequest<R, A, I> {
IronTokenRequest {
registrar: self.registrar.clone(),
authorizer: self.authorizer.clone(),
issuer: self.issuer.clone() }
}
pub fn guard<T>(&self, scopes: T) -> IronGuard<I> where T: IntoIterator<Item=Scope> {
IronGuard { issuer: self.issuer.clone(), scopes: scopes.into_iter().collect() }
}
pub fn registrar(&self) -> LockResult<MutexGuard<R>> {
self.registrar.lock()
}
pub fn authorizer(&self) -> LockResult<MutexGuard<A>> {
self.authorizer.lock()
}
pub fn issuer(&self) -> LockResult<MutexGuard<I>> {
self.issuer.lock()
}
}
impl From<OAuthError> for IronError {
fn from(this: OAuthError) -> IronError {
IronError::new(this, status::Unauthorized)
}
}
impl<PH, R, A> Handler for IronAuthorizer<PH, R, A> where
PH: GenericOwnerAuthorizer + Send + Sync + 'static,
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static
{
fn handle<'a>(&'a self, req: &mut Request) -> IronResult<Response> {
let mut locked_registrar = self.registrar.lock().unwrap();
let mut locked_authorizer = self.authorizer.lock().unwrap();
let authorization_flow = AuthorizationFlow::new(
locked_registrar.deref_mut(), locked_authorizer.deref_mut());
let handler = SpecificOwnerAuthorizer(self.page_handler.as_ref(), PhantomData);
authorization_flow.handle(req, &handler)
}
}
impl<R, A, I> Handler for IronTokenRequest<R, A, I> where
R: Registrar + Send + 'static,
A: Authorizer + Send + 'static,
I: Issuer + Send + 'static
{
fn handle<'a>(&'a self, request: &mut Request) -> IronResult<Response> {
let mut locked_registrar = self.registrar.lock().unwrap();
let mut locked_authorizer = self.authorizer.lock().unwrap();
let mut locked_issuer = self.issuer.lock().unwrap();
GrantFlow::new(
locked_registrar.deref_mut(),
locked_authorizer.deref_mut(),
locked_issuer.deref_mut())
.handle(request)
}
}
impl<I> BeforeMiddleware for IronGuard<I> where
I: Issuer + Send + 'static
{
fn before(&self, request: &mut Request) -> IronResult<()> {
let mut locked_issuer = self.issuer.lock().unwrap();
AccessFlow::new(locked_issuer.deref_mut(), &self.scopes)
.handle(request).into()
}
}
pub mod prelude {
pub use url::Url;
pub use code_grant::prelude::*;
pub use super::{IronGranter, IronOwnerAuthorizer, PreGrant, Authentication, OAuthError};
}