Skip to content

Commit

Permalink
simplfy role-auth middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
kenpaicat committed Sep 23, 2023
1 parent e8d33ef commit f8ecc1a
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 202 deletions.
74 changes: 25 additions & 49 deletions crates/laguna-backend-middleware/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,17 @@ use actix_web::{Error, HttpMessage, HttpResponse, ResponseError};
use std::fmt;

use futures_util::future::LocalBoxFuture;
use jwt_compact::alg::{Hs256, Hs256Key};
use jwt_compact::{AlgorithmExt, UntrustedToken, ValidationError};

use laguna_backend_dto::user::UserDTO;
use laguna_backend_model::role::Role;
use std::future::ready;
use std::future::Ready;

use crate::consts::ACCESS_TOKEN_HEADER_NAME;

pub struct AuthorizationMiddlewareFactory {
min_role: Role,
key: Hs256Key,
}
pub struct AuthorizationMiddlewareFactory(Role);

impl AuthorizationMiddlewareFactory {
pub fn new(key: Hs256Key, min_role: Role) -> Self {
Self { key, min_role }
pub fn new(min_role: Role) -> Self {
Self(min_role)
}
}

Expand All @@ -43,24 +37,21 @@ where

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(AuthorizationMiddleware {
min_role: self.min_role,
key: self.key.clone(),
min_role: self.0,
service,
}))
}
}

pub struct AuthorizationMiddleware<S> {
min_role: Role,
key: Hs256Key,
service: S,
}

#[derive(Debug)]
pub enum AuthorizationError {
UnauthorizedRole { min_role: Role, actual_role: Role },
NoToken,
Invalid(ValidationError),
}

impl fmt::Display for AuthorizationError {
Expand All @@ -79,9 +70,6 @@ impl fmt::Display for AuthorizationError {
Self::NoToken => {
write!(f, "No token")
},
Self::Invalid(err) => {
write!(f, "Invalid token: {}", err)
},
}
}
}
Expand All @@ -91,15 +79,13 @@ impl ResponseError for AuthorizationError {
match self {
Self::UnauthorizedRole { .. } => StatusCode::UNAUTHORIZED,
Self::NoToken => StatusCode::UNAUTHORIZED,
Self::Invalid(_) => StatusCode::UNAUTHORIZED,
}
}

fn error_response(&self) -> HttpResponse {
match self {
Self::UnauthorizedRole { .. } => HttpResponse::Unauthorized().body(format!("{}", self)),
Self::NoToken => HttpResponse::Unauthorized().body(format!("{}", self)),
Self::Invalid(_) => HttpResponse::Unauthorized().body(format!("{}", self)),
}
}
}
Expand All @@ -117,37 +103,27 @@ where
forward_ready!(service);

fn call(&self, req: ServiceRequest) -> Self::Future {
log::info!("{:?}", req.extensions());
// Token has already been validated & verified by AuthenticationService by the time it reaches this middleware.
let access_token_header = req.headers().get(ACCESS_TOKEN_HEADER_NAME);
if let Some(access_token_header) = access_token_header {
// SECURITY: Token is trusted at this point but additional verification is however still performed.
// NOTE: This is probably not a huge bottleneck and is a consequence of using external libraries for authentication (not authorization).
let access_token = UntrustedToken::new(access_token_header.to_str().unwrap()).unwrap();
let signed_access_token = Hs256
.validate_for_signed_token::<UserDTO>(&access_token, &self.key)
.map_err(AuthorizationError::Invalid);
return match signed_access_token {
Ok(signed_access_token) => {
let min_role = self.min_role;
let role = signed_access_token.token.claims().custom.role;
if role < min_role {
return Box::pin(async move {
Result::<Self::Response, Self::Error>::Err(
AuthorizationError::UnauthorizedRole {
min_role,
actual_role: role,
}
.into(),
)
});
let user_role = if let Some(user) = req.extensions().get::<UserDTO>() {
user.role
} else {
return Box::pin(async move {
Result::<Self::Response, Self::Error>::Err(AuthorizationError::NoToken.into())

Check warning on line 110 in crates/laguna-backend-middleware/src/auth.rs

View check run for this annotation

Codecov / codecov/patch

crates/laguna-backend-middleware/src/auth.rs#L109-L110

Added lines #L109 - L110 were not covered by tests
});
};
let min_role = self.min_role;
if user_role < min_role {
Box::pin(async move {
Result::<Self::Response, Self::Error>::Err(
AuthorizationError::UnauthorizedRole {
min_role,
actual_role: user_role,
}
let fut = self.service.call(req);
Box::pin(fut)
},
Err(err) => Box::pin(async move { Result::<Self::Response, Self::Error>::Err(err.into()) }),
};
.into(),

Check warning on line 121 in crates/laguna-backend-middleware/src/auth.rs

View check run for this annotation

Codecov / codecov/patch

crates/laguna-backend-middleware/src/auth.rs#L121

Added line #L121 was not covered by tests
)
})
} else {
let fut = self.service.call(req);
Box::pin(fut)
}
Box::pin(async move { Err(AuthorizationError::NoToken.into()) })
}
}
23 changes: 0 additions & 23 deletions crates/laguna-backend-middleware/src/auth_helper.rs

This file was deleted.

117 changes: 0 additions & 117 deletions crates/laguna-backend-middleware/src/exclusive.rs

This file was deleted.

2 changes: 0 additions & 2 deletions crates/laguna-backend-middleware/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
#![doc(html_favicon_url = "https://sloveniaengineering.github.io/laguna-backend/favicon.ico")]
#![doc(issue_tracker_base_url = "https://github.com/SloveniaEngineering/laguna-backend")]
pub mod auth;
pub mod auth_helper;
pub mod consts;
pub mod exclusive;
pub mod hexify;
pub mod mime;
19 changes: 8 additions & 11 deletions crates/laguna-backend-setup/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,7 @@ pub fn get_config_fn(settings: Settings) -> impl FnOnce(&mut ServiceConfig) {
"/{id}",
web::patch()
.to(user_patch)
.wrap(AuthorizationMiddlewareFactory::new(
secret_key.clone(),
Role::Mod,
)),
.wrap(AuthorizationMiddlewareFactory::new(Role::Mod)),
)
.route("/{id}/role_change", web::patch().to(user_role_change))
.route("/me", web::get().to(user_me_get))
Expand All @@ -188,9 +185,9 @@ pub fn get_config_fn(settings: Settings) -> impl FnOnce(&mut ServiceConfig) {
.route("/{info_hash}", web::get().to(torrent_get::<SHA1_LENGTH>))
.route(
"/",
web::put().to(torrent_put::<SHA1_LENGTH>).wrap(
AuthorizationMiddlewareFactory::new(secret_key.clone(), Role::Verified),
),
web::put()
.to(torrent_put::<SHA1_LENGTH>)
.wrap(AuthorizationMiddlewareFactory::new(Role::Verified)),
)
.route("/rating", web::post().to(rating_create::<SHA1_LENGTH>))
.route(
Expand All @@ -203,15 +200,15 @@ pub fn get_config_fn(settings: Settings) -> impl FnOnce(&mut ServiceConfig) {
)
.route(
"/{info_hash}",
web::patch().to(torrent_patch::<SHA1_LENGTH>).wrap(
AuthorizationMiddlewareFactory::new(secret_key.clone(), Role::Mod),
),
web::patch()
.to(torrent_patch::<SHA1_LENGTH>)
.wrap(AuthorizationMiddlewareFactory::new(Role::Mod)),
)
.route(
"/{info_hash}",
web::delete()
.to(torrent_delete::<SHA1_LENGTH>)
.wrap(AuthorizationMiddlewareFactory::new(secret_key, Role::Mod)),
.wrap(AuthorizationMiddlewareFactory::new(Role::Mod)),
)
.route(
"/{info_hash}/swarm",
Expand Down

0 comments on commit f8ecc1a

Please sign in to comment.