mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-23 04:52:28 +00:00
add AuthResult resource result type
This commit is contained in:
parent
756fd3a98a
commit
48867da535
3 changed files with 175 additions and 6 deletions
|
@ -308,6 +308,7 @@ mod test
|
||||||
// some known tokens
|
// some known tokens
|
||||||
const VALID_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9.8h8Ax-nnykqEQ62t7CxmM3ja6NzUQ4L0MLOOzddjLKk";
|
const VALID_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9.8h8Ax-nnykqEQ62t7CxmM3ja6NzUQ4L0MLOOzddjLKk";
|
||||||
const EXPIRED_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjE1Nzc4MzcxMDB9.eV1snaGLYrJ7qUoMk74OvBY3WUU9M0Je5HTU2xtX1v0";
|
const EXPIRED_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjE1Nzc4MzcxMDB9.eV1snaGLYrJ7qUoMk74OvBY3WUU9M0Je5HTU2xtX1v0";
|
||||||
|
const INVALID_TOKEN : &'static str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
struct TestData
|
struct TestData
|
||||||
|
@ -332,15 +333,27 @@ mod test
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct TestHandler;
|
struct NoneAuthHandler;
|
||||||
impl<T> AuthHandler<T> for TestHandler
|
impl<T> AuthHandler<T> for NoneAuthHandler
|
||||||
{
|
{
|
||||||
fn jwt_secret<F : FnOnce() -> Option<T>>(&self, _state : &mut State, _decode_data : F) -> Option<Vec<u8>>
|
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)]
|
#[derive(Default)]
|
||||||
struct TestAssertingHandler;
|
struct TestAssertingHandler;
|
||||||
impl<T> AuthHandler<T> for 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
|
where T : DeserializeOwned + Send
|
||||||
{
|
{
|
||||||
AuthMiddleware::from_source(source)
|
AuthMiddleware::new(source, Default::default(), StaticAuthHandler::from_array(JWT_SECRET))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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]
|
#[test]
|
||||||
fn test_auth_middleware_auth_header_token()
|
fn test_auth_middleware_auth_header_token()
|
||||||
{
|
{
|
||||||
|
|
|
@ -170,6 +170,8 @@ pub use resource::{
|
||||||
|
|
||||||
mod result;
|
mod result;
|
||||||
pub use result::{
|
pub use result::{
|
||||||
|
AuthResult,
|
||||||
|
AuthResult::AuthErr,
|
||||||
NoContent,
|
NoContent,
|
||||||
Raw,
|
Raw,
|
||||||
ResourceResult,
|
ResourceResult,
|
||||||
|
|
|
@ -7,7 +7,10 @@ use mime::{Mime, APPLICATION_JSON, STAR_STAR};
|
||||||
use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty};
|
use openapiv3::{SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::error::Error as SerdeJsonError;
|
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.
|
/// A response, used to create the final gotham response from.
|
||||||
pub struct Response
|
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)]
|
#[cfg(test)]
|
||||||
fn full_body(self) -> Vec<u8>
|
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>
|
impl<T : ResponseBody> ResourceResult for Success<T>
|
||||||
{
|
{
|
||||||
fn into_response(self) -> Result<Response, SerdeJsonError>
|
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
|
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
|
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>
|
impl<T : Into<Body>> ResourceResult for Raw<T>
|
||||||
{
|
{
|
||||||
fn into_response(self) -> Result<Response, SerdeJsonError>
|
fn into_response(self) -> Result<Response, SerdeJsonError>
|
||||||
|
|
Loading…
Add table
Reference in a new issue