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
							
								
									7c24144b74
								
							
						
					
					
						commit
						bd97a88d2b
					
				@ -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