Implement per-user guards for the user details endpoint

This commit is contained in:
Valentin Tolmer 2021-06-30 22:07:23 +02:00 committed by nitnelave
parent c7fb8baadb
commit 6744a89922
3 changed files with 36 additions and 8 deletions

View File

@ -340,6 +340,7 @@ where
pub async fn token_validator<Backend>( pub async fn token_validator<Backend>(
req: ServiceRequest, req: ServiceRequest,
credentials: BearerAuth, credentials: BearerAuth,
admin_required: bool,
) -> Result<ServiceRequest, actix_web::Error> ) -> Result<ServiceRequest, actix_web::Error>
where where
Backend: TcpBackendHandler + BackendHandler + 'static, Backend: TcpBackendHandler + BackendHandler + 'static,
@ -360,17 +361,39 @@ where
if state.jwt_blacklist.read().unwrap().contains(&jwt_hash) { if state.jwt_blacklist.read().unwrap().contains(&jwt_hash) {
return Err(ErrorUnauthorized("JWT was logged out")); return Err(ErrorUnauthorized("JWT was logged out"));
} }
let groups = &token.claims().groups; let is_admin = token.claims().groups.contains("lldap_admin");
if groups.contains("lldap_admin") { if is_admin
|| (!admin_required && req.match_info().get("user_id") == Some(&token.claims().user))
{
debug!("Got authorized token for user {}", &token.claims().user); debug!("Got authorized token for user {}", &token.claims().user);
Ok(req) Ok(req)
} else { } else {
Err(ErrorUnauthorized( Err(ErrorUnauthorized(
"JWT error: User is not in group lldap_admin", "JWT error: User is not authorized to access this resource",
)) ))
} }
} }
pub async fn admin_token_validator<Backend>(
req: ServiceRequest,
credentials: BearerAuth,
) -> Result<ServiceRequest, actix_web::Error>
where
Backend: TcpBackendHandler + BackendHandler + 'static,
{
token_validator::<Backend>(req, credentials, true).await
}
pub async fn user_token_validator<Backend>(
req: ServiceRequest,
credentials: BearerAuth,
) -> Result<ServiceRequest, actix_web::Error>
where
Backend: TcpBackendHandler + BackendHandler + 'static,
{
token_validator::<Backend>(req, credentials, false).await
}
pub fn configure_server<Backend>(cfg: &mut web::ServiceConfig) pub fn configure_server<Backend>(cfg: &mut web::ServiceConfig)
where where
Backend: TcpBackendHandler + LoginHandler + OpaqueHandler + BackendHandler + 'static, Backend: TcpBackendHandler + LoginHandler + OpaqueHandler + BackendHandler + 'static,

View File

@ -1,11 +1,13 @@
use crate::{ use crate::{
domain::{error::DomainError, handler::*}, domain::{error::DomainError, handler::*},
infra::{ infra::{
auth_service,
tcp_backend_handler::*, tcp_backend_handler::*,
tcp_server::{error_to_http_response, AppState}, tcp_server::{error_to_http_response, AppState},
}, },
}; };
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use actix_web_httpauth::middleware::HttpAuthentication;
pub(crate) fn error_to_api_response<T>(error: DomainError) -> ApiResult<T> { pub(crate) fn error_to_api_response<T>(error: DomainError) -> ApiResult<T> {
ApiResult::Right(error_to_http_response(error)) ApiResult::Right(error_to_http_response(error))
@ -76,10 +78,17 @@ where
}); });
cfg.app_data(json_config); cfg.app_data(json_config);
cfg.service( cfg.service(
web::resource("/user/{user_id}").route(web::get().to(user_details_handler::<Backend>)), web::resource("/user/{user_id}")
.route(web::get().to(user_details_handler::<Backend>))
.wrap(HttpAuthentication::bearer(
auth_service::user_token_validator::<Backend>,
)),
); );
cfg.service( cfg.service(
web::scope("/users") web::scope("/users")
.wrap(HttpAuthentication::bearer(
auth_service::admin_token_validator::<Backend>,
))
.guard(actix_web::guard::Header("content-type", "application/json")) .guard(actix_web::guard::Header("content-type", "application/json"))
.service(web::resource("").route(web::post().to(user_list_handler::<Backend>))) .service(web::resource("").route(web::post().to(user_list_handler::<Backend>)))
.service( .service(

View File

@ -11,7 +11,6 @@ use actix_http::HttpServiceBuilder;
use actix_server::ServerBuilder; use actix_server::ServerBuilder;
use actix_service::map_config; use actix_service::map_config;
use actix_web::{dev::AppConfig, web, App, HttpRequest, HttpResponse}; use actix_web::{dev::AppConfig, web, App, HttpRequest, HttpResponse};
use actix_web_httpauth::middleware::HttpAuthentication;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use hmac::{Hmac, NewMac}; use hmac::{Hmac, NewMac};
use sha2::Sha512; use sha2::Sha512;
@ -64,9 +63,6 @@ fn http_config<Backend>(
// API endpoint. // API endpoint.
.service( .service(
web::scope("/api") web::scope("/api")
.wrap(HttpAuthentication::bearer(
auth_service::token_validator::<Backend>,
))
.wrap(auth_service::CookieToHeaderTranslatorFactory) .wrap(auth_service::CookieToHeaderTranslatorFactory)
.configure(tcp_api::api_config::<Backend>), .configure(tcp_api::api_config::<Backend>),
) )