mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
infra: Remove the TCP API
Deprecated in favor of GraphQL
This commit is contained in:
parent
651adbe3c8
commit
08a3845cbe
@ -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,
|
||||||
|
@ -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>))
|
||||||
|
@ -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;
|
||||||
|
@ -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()
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"))
|
||||||
|
Loading…
Reference in New Issue
Block a user