use primitives::authorizer::Authorizer;
use primitives::registrar::{PreGrant, ClientUrl, Registrar, RegistrarError};
use primitives::grant::{Extensions, Grant};
use primitives::issuer::{IssuedToken, Issuer};
use primitives::scope::Scope;
use super::error::{AccessTokenError, AccessTokenErrorExt, AccessTokenErrorType};
use super::error::{AuthorizationError, AuthorizationErrorExt, AuthorizationErrorType};
use super::extensions::{AccessTokenExtension, CodeExtension};
use std::borrow::Cow;
use std::collections::HashMap;
use url::Url;
use chrono::{Duration, Utc};
use serde_json;
pub enum CodeError {
Ignore ,
Redirect(ErrorUrl) ,
}
pub struct ErrorUrl {
base_uri: Url,
error: AuthorizationError,
}
pub enum IssuerError {
Invalid(ErrorDescription),
Unauthorized(ErrorDescription, String),
}
pub struct ErrorDescription {
error: AccessTokenError,
}
pub enum AccessError {
InvalidRequest,
AccessDenied,
}
type CodeResult<T> = Result<T, CodeError>;
type AccessTokenResult<T> = Result<T, IssuerError>;
type AccessResult<T> = Result<T, AccessError>;
impl ErrorUrl {
fn new<S>(mut url: Url, state: Option<S>, error: AuthorizationError) -> ErrorUrl where S: AsRef<str> {
url.query_pairs_mut()
.extend_pairs(state.as_ref().map(|st| ("state", st.as_ref())));
ErrorUrl{ base_uri: url, error: error }
}
pub fn with_mut<M>(&mut self, modifier: M) where M: AuthorizationErrorExt {
modifier.modify(&mut self.error);
}
pub fn with<M>(mut self, modifier: M) -> Self where M: AuthorizationErrorExt {
modifier.modify(&mut self.error);
self
}
}
impl Into<Url> for ErrorUrl {
fn into(self) -> Url {
let mut url = self.base_uri;
url.query_pairs_mut()
.extend_pairs(self.error.into_iter());
url
}
}
impl IssuerError {
fn invalid<Mod>(modifier: Mod) -> IssuerError where Mod: AccessTokenErrorExt {
IssuerError::Invalid(ErrorDescription{
error: AccessTokenError::with((AccessTokenErrorType::InvalidRequest, modifier))
})
}
fn unauthorized<Mod>(modifier: Mod, authtype: &str) -> IssuerError where Mod: AccessTokenErrorExt {
IssuerError::Unauthorized(
ErrorDescription{error: AccessTokenError::with((AccessTokenErrorType::InvalidClient, modifier))},
authtype.to_string())
}
}
impl ErrorDescription {
pub fn to_json(self) -> String {
use std::iter::IntoIterator;
use std::collections::HashMap;
let asmap = self.error.into_iter()
.map(|(k, v)| (k.to_string(), v.into_owned()))
.collect::<HashMap<String, String>>();
serde_json::to_string(&asmap).unwrap()
}
}
pub struct BearerToken(IssuedToken, String);
impl BearerToken {
pub fn to_json(self) -> String {
let remaining = self.0.until.signed_duration_since(Utc::now());
let kvmap: HashMap<_, _> = vec![
("access_token", self.0.token),
("refresh_token", self.0.refresh),
("token_type", "bearer".to_string()),
("expires_in", remaining.num_seconds().to_string()),
("scope", self.1)].into_iter().collect();
serde_json::to_string(&kvmap).unwrap()
}
}
pub trait CodeRequest {
fn valid(&self) -> bool;
fn client_id(&self) -> Option<Cow<str>>;
fn scope(&self) -> Option<Cow<str>>;
fn redirect_uri(&self) -> Option<Cow<str>>;
fn state(&self) -> Option<Cow<str>>;
fn method(&self) -> Option<Cow<str>>;
fn extension(&self, &str) -> Option<Cow<str>>;
}
pub struct CodeRef<'a> {
registrar: &'a Registrar,
authorizer: &'a mut Authorizer,
}
pub struct AuthorizationRequest<'a> {
pre_grant: PreGrant,
code: CodeRef<'a>,
state: Option<String>,
extensions: Extensions,
}
impl<'l> CodeRequest for &'l CodeRequest {
fn valid(&self) -> bool {
(*self).valid()
}
fn client_id(&self) -> Option<Cow<str>> {
(*self).client_id()
}
fn scope(&self) -> Option<Cow<str>> {
(*self).scope()
}
fn redirect_uri(&self) -> Option<Cow<str>> {
(*self).redirect_uri()
}
fn state(&self) -> Option<Cow<str>> {
(*self).state()
}
fn method(&self) -> Option<Cow<str>> {
(*self).method()
}
fn extension(&self, key: &str) -> Option<Cow<str>> {
(*self).extension(key)
}
}
impl<'u> CodeRef<'u> {
pub fn negotiate(self, request: &CodeRequest, extensions: &[&CodeExtension])
-> CodeResult<AuthorizationRequest<'u>> {
if !request.valid() {
return Err(CodeError::Ignore)
}
let client_id = request.client_id().ok_or(CodeError::Ignore)?;
let redirect_uri = match request.redirect_uri() {
None => None,
Some(ref uri) => {
let parsed = Url::parse(&uri).map_err(|_| CodeError::Ignore)?;
Some(Cow::Owned(parsed))
},
};
let client_url = ClientUrl {
client_id,
redirect_uri,
};
let bound_client = match self.registrar.bound_redirect(client_url) {
Err(RegistrarError::Unregistered) => return Err(CodeError::Ignore),
Err(RegistrarError::MismatchedRedirect) => return Err(CodeError::Ignore),
Err(RegistrarError::UnauthorizedClient) => return Err(CodeError::Ignore),
Ok(pre_grant) => pre_grant,
};
let state = request.state();
let error_uri = bound_client.redirect_uri.clone().into_owned();
let prepared_error = ErrorUrl::new(error_uri.clone(), state.clone(),
AuthorizationError::with(()));
match request.method() {
Some(ref method) if method.as_ref() == "code"
=> (),
_ => return Err(CodeError::Redirect(prepared_error.with(
AuthorizationErrorType::UnsupportedResponseType))),
}
let scope = request.scope();
let scope = match scope.map(|scope| scope.as_ref().parse()) {
None => None,
Some(Err(_)) =>
return Err(CodeError::Redirect(prepared_error.with(
AuthorizationErrorType::InvalidScope))),
Some(Ok(scope)) => Some(scope),
};
let mut grant_extensions = Extensions::new();
for extension_instance in extensions {
match extension_instance.extend_code(request) {
Err(_) =>
return Err(CodeError::Redirect(prepared_error.with(
AuthorizationErrorType::InvalidRequest))),
Ok(Some(extension)) =>
grant_extensions.set(extension_instance, extension),
Ok(None) => (),
}
}
Ok(AuthorizationRequest {
pre_grant: bound_client.negotiate(scope),
code: CodeRef { registrar: self.registrar, authorizer: self.authorizer },
state: state.map(|cow| cow.into_owned()),
extensions: grant_extensions,
})
}
pub fn with(registrar: &'u Registrar, t: &'u mut Authorizer) -> Self {
CodeRef { registrar, authorizer: t }
}
}
impl<'a> AuthorizationRequest<'a> {
pub fn deny(self) -> CodeResult<Url> {
let url = self.pre_grant.redirect_uri;
let error = AuthorizationError::with(AuthorizationErrorType::AccessDenied);
let error = ErrorUrl::new(url, self.state, error);
Err(CodeError::Redirect(error))
}
pub fn authorize(self, owner_id: Cow<'a, str>) -> CodeResult<Url> {
let mut url = self.pre_grant.redirect_uri.clone();
let grant = self.code.authorizer.authorize(Grant {
owner_id: owner_id.into_owned(),
client_id: self.pre_grant.client_id,
redirect_uri: self.pre_grant.redirect_uri,
scope: self.pre_grant.scope,
until: Utc::now() + Duration::minutes(10),
extensions: self.extensions,
}).map_err(|()| CodeError::Ignore)?;
url.query_pairs_mut()
.append_pair("code", grant.as_str())
.extend_pairs(self.state.map(|v| ("state", v)))
.finish();
Ok(url)
}
pub fn pre_grant(&self) -> &PreGrant {
&self.pre_grant
}
}
pub struct IssuerRef<'a> {
registrar: &'a Registrar,
authorizer: &'a mut Authorizer,
issuer: &'a mut Issuer,
}
pub trait AccessTokenRequest {
fn valid(&self) -> bool;
fn code(&self) -> Option<Cow<str>>;
fn authorization(&self) -> Option<(Cow<str>, Cow<[u8]>)>;
fn client_id(&self) -> Option<Cow<str>>;
fn redirect_uri(&self) -> Option<Cow<str>>;
fn grant_type(&self) -> Option<Cow<str>>;
fn extension(&self, &str) -> Option<Cow<str>>;
}
impl<'u> IssuerRef<'u> {
pub fn use_code(&mut self, request: &AccessTokenRequest, extensions: &[&AccessTokenExtension])
-> AccessTokenResult<BearerToken> {
if !request.valid() {
return Err(IssuerError::invalid(()))
}
let authorization = request.authorization();
let client_id = request.client_id();
let (client_id, auth): (&str, Option<&[u8]>) = match (&client_id, &authorization) {
(&None, &Some((ref client_id, ref auth))) => (client_id.as_ref(), Some(auth.as_ref())),
(&Some(ref client_id), &None) => (client_id.as_ref(), None),
_ => return Err(IssuerError::invalid(())),
};
let client = self.registrar.client(&client_id).ok_or(
IssuerError::unauthorized((), "basic"))?;
client.check_authentication(auth).map_err(|_|
IssuerError::unauthorized((), "basic"))?;
match request.grant_type() {
Some(ref cow) if cow == "authorization_code" => (),
None => return Err(IssuerError::invalid(())),
Some(_) => return Err(IssuerError::invalid(AccessTokenErrorType::UnsupportedGrantType)),
};
let code = request.code()
.ok_or(IssuerError::invalid(()))?;
let code = code.as_ref();
let saved_params = match self.authorizer.extract(code) {
None => return Err(IssuerError::invalid(())),
Some(v) => v,
};
let redirect_uri = request.redirect_uri()
.ok_or(IssuerError::invalid(()))?;
let redirect_uri = redirect_uri.as_ref();
if (saved_params.client_id.as_ref(), saved_params.redirect_uri.as_str()) != (client_id, redirect_uri) {
return Err(IssuerError::invalid(AccessTokenErrorType::InvalidGrant))
}
if saved_params.until < Utc::now() {
return Err(IssuerError::invalid((AccessTokenErrorType::InvalidGrant, "Grant expired")).into())
}
let mut code_extensions = saved_params.extensions;
let mut access_extensions = Extensions::new();
for extension_instance in extensions {
let saved_extension = code_extensions.remove(extension_instance);
match extension_instance.extend_access_token(request, saved_extension) {
Err(_) => return Err(IssuerError::invalid(())),
Ok(Some(extension)) => access_extensions.set(extension_instance, extension),
Ok(None) => (),
}
}
let token = self.issuer.issue(Grant {
client_id: saved_params.client_id,
owner_id: saved_params.owner_id,
redirect_uri: saved_params.redirect_uri,
scope: saved_params.scope.clone(),
until: Utc::now() + Duration::hours(1),
extensions: access_extensions,
}).map_err(|()| IssuerError::invalid((
AccessTokenErrorType::InvalidRequest,
"Failed to generate issued tokens"
)))?;
Ok(BearerToken{ 0: token, 1: saved_params.scope.to_string() })
}
pub fn with(r: &'u Registrar, t: &'u mut Authorizer, i: &'u mut Issuer) -> Self {
IssuerRef { registrar: r, authorizer: t, issuer: i }
}
}
pub struct GuardRef<'a> {
scopes: &'a [Scope],
issuer: &'a mut Issuer,
}
pub trait GuardRequest {
fn valid(&self) -> bool;
fn token(&self) -> Option<Cow<str>>;
}
impl<'a> GuardRef<'a> {
pub fn protect(&self, req: &GuardRequest)
-> AccessResult<()> {
if !req.valid() {
return Err(AccessError::InvalidRequest)
}
let token = req.token()
.ok_or(AccessError::AccessDenied)?;
let grant = self.issuer.recover_token(&token)
.ok_or(AccessError::AccessDenied)?;
if grant.until < Utc::now() {
return Err(AccessError::AccessDenied);
}
if !self.scopes.iter()
.any(|resource_scope| resource_scope.allow_access(&grant.scope)) {
return Err(AccessError::AccessDenied);
}
return Ok(())
}
pub fn with<S: ?Sized>(issuer: &'a mut Issuer, scopes: &'a S) -> Self
where S: AsRef<[Scope]> {
GuardRef { scopes: scopes.as_ref(), issuer: issuer }
}
}