infra: Remove the TCP API

Deprecated in favor of GraphQL
This commit is contained in:
Valentin Tolmer 2021-08-30 10:18:35 +02:00 committed by nitnelave
parent 651adbe3c8
commit 08a3845cbe
5 changed files with 21 additions and 197 deletions

View File

@ -5,7 +5,6 @@ use crate::{
opaque_handler::OpaqueHandler, opaque_handler::OpaqueHandler,
}, },
infra::{ infra::{
tcp_api::{error_to_api_response, ApiResult},
tcp_backend_handler::*, tcp_backend_handler::*,
tcp_server::{error_to_http_response, AppState}, tcp_server::{error_to_http_response, AppState},
}, },
@ -16,7 +15,6 @@ use actix_web::{
error::{ErrorBadRequest, ErrorUnauthorized}, error::{ErrorBadRequest, ErrorUnauthorized},
web, HttpRequest, HttpResponse, web, HttpRequest, HttpResponse,
}; };
use actix_web_httpauth::extractors::bearer::BearerAuth;
use anyhow::Result; use anyhow::Result;
use chrono::prelude::*; use chrono::prelude::*;
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
@ -24,7 +22,6 @@ use futures_util::{FutureExt, TryFutureExt};
use hmac::Hmac; use hmac::Hmac;
use jwt::{SignWithKey, VerifyWithKey}; use jwt::{SignWithKey, VerifyWithKey};
use lldap_model::{login, registration, JWTClaims}; use lldap_model::{login, registration, JWTClaims};
use log::*;
use sha2::Sha512; use sha2::Sha512;
use std::collections::{hash_map::DefaultHasher, HashSet}; use std::collections::{hash_map::DefaultHasher, HashSet};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -167,6 +164,12 @@ where
.finish() .finish()
} }
pub(crate) fn error_to_api_response<T>(error: DomainError) -> ApiResult<T> {
ApiResult::Right(error_to_http_response(error))
}
pub type ApiResult<M> = actix_web::Either<web::Json<M>, HttpResponse>;
async fn opaque_login_start<Backend>( async fn opaque_login_start<Backend>(
data: web::Data<AppState<Backend>>, data: web::Data<AppState<Backend>>,
request: web::Json<login::ClientLoginStartRequest>, request: web::Json<login::ClientLoginStartRequest>,
@ -386,48 +389,6 @@ pub(crate) fn check_if_token_is_valid<Backend>(
}) })
} }
pub async fn token_validator<Backend>(
req: ServiceRequest,
credentials: BearerAuth,
admin_required: bool,
) -> Result<ServiceRequest, actix_web::Error>
where
Backend: TcpBackendHandler + BackendHandler + 'static,
{
let state = req
.app_data::<web::Data<AppState<Backend>>>()
.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<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

@ -77,6 +77,19 @@ pub fn configure_endpoint<Backend>(cfg: &mut web::ServiceConfig)
where where
Backend: BackendHandler + Sync + 'static, 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( cfg.service(
web::resource("/graphql") web::resource("/graphql")
.route(web::post().to(graphql_route::<Backend>)) .route(web::post().to(graphql_route::<Backend>))

View File

@ -8,6 +8,5 @@ pub mod ldap_handler;
pub mod ldap_server; pub mod ldap_server;
pub mod logging; pub mod logging;
pub mod sql_backend_handler; pub mod sql_backend_handler;
pub mod tcp_api;
pub mod tcp_backend_handler; pub mod tcp_backend_handler;
pub mod tcp_server; pub mod tcp_server;

View File

@ -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<T>(error: DomainError) -> ApiResult<T> {
ApiResult::Right(error_to_http_response(error))
}
pub type ApiResult<M> = actix_web::Either<web::Json<M>, HttpResponse>;
async fn user_list_handler<Backend>(
data: web::Data<AppState<Backend>>,
info: web::Json<ListUsersRequest>,
) -> ApiResult<Vec<User>>
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<Backend>(
data: web::Data<AppState<Backend>>,
request: HttpRequest,
) -> ApiResult<User>
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<Backend>(
data: web::Data<AppState<Backend>>,
info: web::Json<CreateUserRequest>,
) -> 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<Backend>(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::<Backend>(cfg);
cfg.service(
web::resource("/user/{user_id}")
.route(web::get().to(user_details_handler::<Backend>))
.wrap(HttpAuthentication::bearer(
auth_service::user_token_validator::<Backend>,
)),
);
cfg.service(
web::scope("/users")
.wrap(HttpAuthentication::bearer(
auth_service::admin_token_validator::<Backend>,
))
.guard(actix_web::guard::Header("content-type", "application/json"))
.service(web::resource("").route(web::post().to(user_list_handler::<Backend>)))
.service(
web::resource("/create").route(web::post().to(create_user_handler::<Backend>)),
),
);
}
#[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<AppState<MockTestTcpBackendHandler>> {
let app_state = AppState::<MockTestTcpBackendHandler> {
backend_handler: handler,
jwt_key: Hmac::new_varkey(b"jwt_secret").unwrap(),
jwt_blacklist: RwLock::new(HashSet::new()),
};
web::Data::<AppState<MockTestTcpBackendHandler>>::new(app_state)
}
fn expect_json<T: std::fmt::Debug>(result: ApiResult<T>) -> 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()
}]
);
}
}

View File

@ -4,7 +4,7 @@ use crate::{
handler::{BackendHandler, LoginHandler}, handler::{BackendHandler, LoginHandler},
opaque_handler::OpaqueHandler, 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_files::{Files, NamedFile};
use actix_http::HttpServiceBuilder; use actix_http::HttpServiceBuilder;
@ -64,7 +64,7 @@ fn http_config<Backend>(
.service( .service(
web::scope("/api") web::scope("/api")
.wrap(auth_service::CookieToHeaderTranslatorFactory) .wrap(auth_service::CookieToHeaderTranslatorFactory)
.configure(tcp_api::api_config::<Backend>), .configure(super::graphql::api::configure_endpoint::<Backend>),
) )
// Serve the /pkg path with the compiled WASM app. // Serve the /pkg path with the compiled WASM app.
.service(Files::new("/pkg", "./app/pkg")) .service(Files::new("/pkg", "./app/pkg"))