1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-22 20:52:27 +00:00

add AuthResult resource result type

This commit is contained in:
Dominic 2020-01-25 15:59:37 +01:00
parent 756fd3a98a
commit 48867da535
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
3 changed files with 175 additions and 6 deletions

View file

@ -308,6 +308,7 @@ mod test
// some known tokens
const VALID_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9.8h8Ax-nnykqEQ62t7CxmM3ja6NzUQ4L0MLOOzddjLKk";
const EXPIRED_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjE1Nzc4MzcxMDB9.eV1snaGLYrJ7qUoMk74OvBY3WUU9M0Je5HTU2xtX1v0";
const INVALID_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9";
#[derive(Debug, Deserialize, PartialEq)]
struct TestData
@ -332,15 +333,27 @@ mod test
}
#[derive(Default)]
struct TestHandler;
impl<T> AuthHandler<T> for TestHandler
struct NoneAuthHandler;
impl<T> AuthHandler<T> for NoneAuthHandler
{
fn jwt_secret<F : FnOnce() -> Option<T>>(&self, _state : &mut State, _decode_data : F) -> Option<Vec<u8>>
{
Some(JWT_SECRET.to_vec())
None
}
}
#[test]
fn test_auth_middleware_none_secret()
{
let middleware = <AuthMiddleware<TestData, NoneAuthHandler>>::from_source(AuthSource::AuthorizationHeader);
State::with_new(|mut state| {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, format!("Bearer {}", VALID_TOKEN).parse().unwrap());
state.put(headers);
middleware.auth_status(&mut state);
});
}
#[derive(Default)]
struct TestAssertingHandler;
impl<T> AuthHandler<T> for TestAssertingHandler
@ -365,10 +378,10 @@ mod test
});
}
fn new_middleware<T>(source : AuthSource) -> AuthMiddleware<T, TestHandler>
fn new_middleware<T>(source : AuthSource) -> AuthMiddleware<T, StaticAuthHandler>
where T : DeserializeOwned + Send
{
AuthMiddleware::from_source(source)
AuthMiddleware::new(source, Default::default(), StaticAuthHandler::from_array(JWT_SECRET))
}
#[test]
@ -400,6 +413,22 @@ mod test
});
}
#[test]
fn test_auth_middleware_invalid_token()
{
let middleware = new_middleware::<TestData>(AuthSource::AuthorizationHeader);
State::with_new(|mut state| {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, format!("Bearer {}", INVALID_TOKEN).parse().unwrap());
state.put(headers);
let status = middleware.auth_status(&mut state);
match status {
AuthStatus::Invalid => {},
_ => panic!("Expected AuthStatus::Invalid, got {:?}", status)
};
});
}
#[test]
fn test_auth_middleware_auth_header_token()
{

View file

@ -170,6 +170,8 @@ pub use resource::{
mod result;
pub use result::{
AuthResult,
AuthResult::AuthErr,
NoContent,
Raw,
ResourceResult,

View file

@ -7,7 +7,10 @@ use mime::{Mime, APPLICATION_JSON, STAR_STAR};
use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty};
use serde::Serialize;
use serde_json::error::Error as SerdeJsonError;
use std::error::Error;
use std::{
error::Error,
fmt::Debug
};
/// A response, used to create the final gotham response from.
pub struct Response
@ -49,6 +52,16 @@ impl Response
}
}
/// Create an empty _403 Forbidden_ `Response`.
pub fn forbidden() -> Self
{
Self {
status: StatusCode::FORBIDDEN,
body: Body::empty(),
mime: None
}
}
#[cfg(test)]
fn full_body(self) -> Vec<u8>
{
@ -170,6 +183,21 @@ impl<T> From<T> for Success<T>
}
}
impl<T : Clone> Clone for Success<T>
{
fn clone(&self) -> Self
{
Self(self.0.clone())
}
}
impl<T : Debug> Debug for Success<T>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Success({:?})", self.0)
}
}
impl<T : ResponseBody> ResourceResult for Success<T>
{
fn into_response(self) -> Result<Response, SerdeJsonError>
@ -189,6 +217,98 @@ impl<T : ResponseBody> ResourceResult for Success<T>
}
}
/**
This return type can be used to map another `ResourceResult` that can only be returned if the
client is authenticated. Otherwise, an empty _403 Forbidden_ response will be issued. Use can
look something like this (assuming the `auth` feature is enabled):
```
# #[macro_use] extern crate gotham_restful_derive;
# use gotham::state::State;
# use gotham_restful::*;
# use serde::Deserialize;
#
# #[derive(Resource)]
# struct MyResource;
#
# #[derive(Clone, Deserialize)]
# struct MyAuthData { exp : u64 }
#
#[rest_read_all(MyResource)]
fn read_all(auth : AuthStatus<MyAuthData>) -> AuthResult<NoContent> {
let auth_data = match auth {
AuthStatus::Authenticated(data) => data,
_ => return AuthErr
};
// do something
NoContent::default().into()
}
```
*/
pub enum AuthResult<T>
{
Ok(T),
AuthErr
}
impl<T> From<T> for AuthResult<T>
{
fn from(t : T) -> Self
{
Self::Ok(t)
}
}
impl<T : Clone> Clone for AuthResult<T>
{
fn clone(&self) -> Self
{
match self {
Self::Ok(t) => Self::Ok(t.clone()),
Self::AuthErr => Self::AuthErr
}
}
}
impl<T : Debug> Debug for AuthResult<T>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ok(t) => write!(f, "Ok({:?})", t),
Self::AuthErr => write!(f, "AuthErr")
}
}
}
impl<T : ResourceResult> ResourceResult for AuthResult<T>
{
fn into_response(self) -> Result<Response, SerdeJsonError>
{
match self
{
Self::Ok(res) => res.into_response(),
Self::AuthErr => Ok(Response::forbidden())
}
}
fn accepted_types() -> Option<Vec<Mime>>
{
T::accepted_types()
}
#[cfg(feature = "openapi")]
fn schema() -> OpenapiSchema
{
T::schema()
}
#[cfg(feature = "openapi")]
fn default_status() -> StatusCode
{
T::default_status()
}
}
/**
This is the return type of a resource that doesn't actually return something. It will result
in a _204 No Content_ answer by default. You don't need to use this type directly if using
@ -282,6 +402,24 @@ impl<T> Raw<T>
}
}
impl<T : Clone> Clone for Raw<T>
{
fn clone(&self) -> Self
{
Self {
raw: self.raw.clone(),
mime: self.mime.clone()
}
}
}
impl<T : Debug> Debug for Raw<T>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Raw({:?}, {:?})", self.raw, self.mime)
}
}
impl<T : Into<Body>> ResourceResult for Raw<T>
{
fn into_response(self) -> Result<Response, SerdeJsonError>