diff --git a/src/infra/auth_service.rs b/src/infra/auth_service.rs index aadf77b..e436617 100644 --- a/src/infra/auth_service.rs +++ b/src/infra/auth_service.rs @@ -5,7 +5,6 @@ use crate::{ opaque_handler::OpaqueHandler, }, infra::{ - tcp_api::{error_to_api_response, ApiResult}, tcp_backend_handler::*, tcp_server::{error_to_http_response, AppState}, }, @@ -16,7 +15,6 @@ use actix_web::{ error::{ErrorBadRequest, ErrorUnauthorized}, web, HttpRequest, HttpResponse, }; -use actix_web_httpauth::extractors::bearer::BearerAuth; use anyhow::Result; use chrono::prelude::*; use futures::future::{ok, Ready}; @@ -24,7 +22,6 @@ use futures_util::{FutureExt, TryFutureExt}; use hmac::Hmac; use jwt::{SignWithKey, VerifyWithKey}; use lldap_model::{login, registration, JWTClaims}; -use log::*; use sha2::Sha512; use std::collections::{hash_map::DefaultHasher, HashSet}; use std::hash::{Hash, Hasher}; @@ -167,6 +164,12 @@ where .finish() } +pub(crate) fn error_to_api_response(error: DomainError) -> ApiResult { + ApiResult::Right(error_to_http_response(error)) +} + +pub type ApiResult = actix_web::Either, HttpResponse>; + async fn opaque_login_start( data: web::Data>, request: web::Json, @@ -386,48 +389,6 @@ pub(crate) fn check_if_token_is_valid( }) } -pub async fn token_validator( - req: ServiceRequest, - credentials: BearerAuth, - admin_required: bool, -) -> Result -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - let state = req - .app_data::>>() - .expect("Invalid app config"); - let ValidationResults { user, is_admin } = check_if_token_is_valid(state, credentials.token())?; - if is_admin || (!admin_required && req.match_info().get("user_id") == Some(&user)) { - debug!("Got authorized token for user {}", &user); - Ok(req) - } else { - Err(ErrorUnauthorized( - "JWT error: User is not authorized to access this resource", - )) - } -} - -pub async fn admin_token_validator( - req: ServiceRequest, - credentials: BearerAuth, -) -> Result -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - token_validator::(req, credentials, true).await -} - -pub async fn user_token_validator( - req: ServiceRequest, - credentials: BearerAuth, -) -> Result -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - token_validator::(req, credentials, false).await -} - pub fn configure_server(cfg: &mut web::ServiceConfig) where Backend: TcpBackendHandler + LoginHandler + OpaqueHandler + BackendHandler + 'static, diff --git a/src/infra/graphql/api.rs b/src/infra/graphql/api.rs index f42c081..2607941 100644 --- a/src/infra/graphql/api.rs +++ b/src/infra/graphql/api.rs @@ -77,6 +77,19 @@ pub fn configure_endpoint(cfg: &mut web::ServiceConfig) where Backend: BackendHandler + Sync + 'static, { + let json_config = web::JsonConfig::default() + .limit(4096) + .error_handler(|err, _req| { + // create custom error response + log::error!("API error: {}", err); + let msg = err.to_string(); + actix_web::error::InternalError::from_response( + err, + HttpResponse::BadRequest().body(msg), + ) + .into() + }); + cfg.app_data(json_config); cfg.service( web::resource("/graphql") .route(web::post().to(graphql_route::)) diff --git a/src/infra/mod.rs b/src/infra/mod.rs index 26d9171..ff2513d 100644 --- a/src/infra/mod.rs +++ b/src/infra/mod.rs @@ -8,6 +8,5 @@ pub mod ldap_handler; pub mod ldap_server; pub mod logging; pub mod sql_backend_handler; -pub mod tcp_api; pub mod tcp_backend_handler; pub mod tcp_server; diff --git a/src/infra/tcp_api.rs b/src/infra/tcp_api.rs deleted file mode 100644 index e687c40..0000000 --- a/src/infra/tcp_api.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::{ - domain::{error::DomainError, handler::*}, - infra::{ - auth_service, - tcp_backend_handler::*, - tcp_server::{error_to_http_response, AppState}, - }, -}; -use actix_web::{web, HttpRequest, HttpResponse}; -use actix_web_httpauth::middleware::HttpAuthentication; - -pub(crate) fn error_to_api_response(error: DomainError) -> ApiResult { - ApiResult::Right(error_to_http_response(error)) -} - -pub type ApiResult = actix_web::Either, HttpResponse>; - -async fn user_list_handler( - data: web::Data>, - info: web::Json, -) -> ApiResult> -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - data.backend_handler - .list_users(info.into_inner()) - .await - .map(|res| ApiResult::Left(web::Json(res))) - .unwrap_or_else(error_to_api_response) -} - -async fn user_details_handler( - data: web::Data>, - request: HttpRequest, -) -> ApiResult -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - let request = UserDetailsRequest { - user_id: request.match_info().get("user_id").unwrap().to_string(), - }; - data.backend_handler - .get_user_details(request) - .await - .map(|res| ApiResult::Left(web::Json(res))) - .unwrap_or_else(error_to_api_response) -} - -async fn create_user_handler( - data: web::Data>, - info: web::Json, -) -> ApiResult<()> -where - Backend: TcpBackendHandler + BackendHandler + 'static, -{ - data.backend_handler - .create_user(info.into_inner()) - .await - .map(|res| ApiResult::Left(web::Json(res))) - .unwrap_or_else(error_to_api_response) -} - -pub fn api_config(cfg: &mut web::ServiceConfig) -where - Backend: TcpBackendHandler + BackendHandler + Sync + 'static, -{ - let json_config = web::JsonConfig::default() - .limit(4096) - .error_handler(|err, _req| { - // create custom error response - log::error!("API error: {}", err); - let msg = err.to_string(); - actix_web::error::InternalError::from_response( - err, - HttpResponse::BadRequest().body(msg), - ) - .into() - }); - cfg.app_data(json_config); - super::graphql::api::configure_endpoint::(cfg); - cfg.service( - web::resource("/user/{user_id}") - .route(web::get().to(user_details_handler::)) - .wrap(HttpAuthentication::bearer( - auth_service::user_token_validator::, - )), - ); - cfg.service( - web::scope("/users") - .wrap(HttpAuthentication::bearer( - auth_service::admin_token_validator::, - )) - .guard(actix_web::guard::Header("content-type", "application/json")) - .service(web::resource("").route(web::post().to(user_list_handler::))) - .service( - web::resource("/create").route(web::post().to(create_user_handler::)), - ), - ); -} - -#[cfg(test)] -mod tests { - use super::*; - use hmac::{Hmac, NewMac}; - use std::collections::HashSet; - use std::sync::RwLock; - - fn get_data( - handler: MockTestTcpBackendHandler, - ) -> web::Data> { - let app_state = AppState:: { - backend_handler: handler, - jwt_key: Hmac::new_varkey(b"jwt_secret").unwrap(), - jwt_blacklist: RwLock::new(HashSet::new()), - }; - web::Data::>::new(app_state) - } - - fn expect_json(result: ApiResult) -> T { - if let ApiResult::Left(res) = result { - res.0 - } else { - panic!("Expected Json result, got: {:?}", result); - } - } - - #[actix_rt::test] - async fn test_user_list_ok() { - let mut backend_handler = MockTestTcpBackendHandler::new(); - backend_handler - .expect_list_users() - .times(1) - .return_once(|_| { - Ok(vec![User { - user_id: "bob".to_string(), - ..Default::default() - }]) - }); - let json = web::Json(ListUsersRequest { filters: None }); - let resp = user_list_handler(get_data(backend_handler), json).await; - assert_eq!( - expect_json(resp), - vec![User { - user_id: "bob".to_string(), - ..Default::default() - }] - ); - } -} diff --git a/src/infra/tcp_server.rs b/src/infra/tcp_server.rs index c4735ee..9476746 100644 --- a/src/infra/tcp_server.rs +++ b/src/infra/tcp_server.rs @@ -4,7 +4,7 @@ use crate::{ handler::{BackendHandler, LoginHandler}, opaque_handler::OpaqueHandler, }, - infra::{auth_service, configuration::Configuration, tcp_api, tcp_backend_handler::*}, + infra::{auth_service, configuration::Configuration, tcp_backend_handler::*}, }; use actix_files::{Files, NamedFile}; use actix_http::HttpServiceBuilder; @@ -64,7 +64,7 @@ fn http_config( .service( web::scope("/api") .wrap(auth_service::CookieToHeaderTranslatorFactory) - .configure(tcp_api::api_config::), + .configure(super::graphql::api::configure_endpoint::), ) // Serve the /pkg path with the compiled WASM app. .service(Files::new("/pkg", "./app/pkg"))