mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	Add TCP handlers for opaque protocol
This commit is contained in:
		
							parent
							
								
									7be0e420d4
								
							
						
					
					
						commit
						4d68a2a015
					
				@ -4,11 +4,11 @@ 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},
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use lldap_model::{JWTClaims, BindRequest};
 | 
					 | 
				
			||||||
use actix_web::{
 | 
					use actix_web::{
 | 
				
			||||||
    cookie::{Cookie, SameSite},
 | 
					    cookie::{Cookie, SameSite},
 | 
				
			||||||
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
 | 
					    dev::{Service, ServiceRequest, ServiceResponse, Transform},
 | 
				
			||||||
@ -22,6 +22,7 @@ use futures::future::{ok, Ready};
 | 
				
			|||||||
use futures_util::{FutureExt, TryFutureExt};
 | 
					use futures_util::{FutureExt, TryFutureExt};
 | 
				
			||||||
use hmac::Hmac;
 | 
					use hmac::Hmac;
 | 
				
			||||||
use jwt::{SignWithKey, VerifyWithKey};
 | 
					use jwt::{SignWithKey, VerifyWithKey};
 | 
				
			||||||
 | 
					use lldap_model::{login, registration, BindRequest, JWTClaims};
 | 
				
			||||||
use log::*;
 | 
					use log::*;
 | 
				
			||||||
use sha2::Sha512;
 | 
					use sha2::Sha512;
 | 
				
			||||||
use std::collections::{hash_map::DefaultHasher, HashSet};
 | 
					use std::collections::{hash_map::DefaultHasher, HashSet};
 | 
				
			||||||
@ -165,30 +166,35 @@ where
 | 
				
			|||||||
        .finish()
 | 
					        .finish()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn post_authorize<Backend>(
 | 
					async fn opaque_login_start<Backend>(
 | 
				
			||||||
    data: web::Data<AppState<Backend>>,
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
    request: web::Json<BindRequest>,
 | 
					    request: web::Json<login::ClientLoginStartRequest>,
 | 
				
			||||||
 | 
					) -> ApiResult<login::ServerLoginStartResponse>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Backend: OpaqueHandler + 'static,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    data.backend_handler
 | 
				
			||||||
 | 
					        .login_start(request.clone())
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .map(|res| ApiResult::Left(web::Json(res)))
 | 
				
			||||||
 | 
					        .unwrap_or_else(error_to_api_response)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn get_login_successful_response<Backend>(
 | 
				
			||||||
 | 
					    data: &web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
					    name: &str,
 | 
				
			||||||
) -> HttpResponse
 | 
					) -> HttpResponse
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    Backend: TcpBackendHandler + BackendHandler + LoginHandler + 'static,
 | 
					    Backend: TcpBackendHandler + BackendHandler,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    let req: BindRequest = request.clone();
 | 
					    // The authentication was successful, we need to fetch the groups to create the JWT
 | 
				
			||||||
    data.backend_handler
 | 
					 | 
				
			||||||
        .bind(req)
 | 
					 | 
				
			||||||
        // If the authentication was successful, we need to fetch the groups to create the JWT
 | 
					 | 
				
			||||||
    // token.
 | 
					    // token.
 | 
				
			||||||
        .and_then(|_| data.backend_handler.get_user_groups(request.name.clone()))
 | 
					 | 
				
			||||||
        .and_then(|g| async {
 | 
					 | 
				
			||||||
            Ok((
 | 
					 | 
				
			||||||
                g,
 | 
					 | 
				
			||||||
    data.backend_handler
 | 
					    data.backend_handler
 | 
				
			||||||
                    .create_refresh_token(&request.name)
 | 
					        .get_user_groups(name.to_string())
 | 
				
			||||||
                    .await?,
 | 
					        .and_then(|g| async { Ok((g, data.backend_handler.create_refresh_token(&name).await?)) })
 | 
				
			||||||
            ))
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .map(|(groups, (refresh_token, max_age))| {
 | 
					        .map(|(groups, (refresh_token, max_age))| {
 | 
				
			||||||
            let token = create_jwt(&data.jwt_key, request.name.clone(), groups);
 | 
					            let token = create_jwt(&data.jwt_key, name.to_string(), groups);
 | 
				
			||||||
            HttpResponse::Ok()
 | 
					            HttpResponse::Ok()
 | 
				
			||||||
                .cookie(
 | 
					                .cookie(
 | 
				
			||||||
                    Cookie::build("token", token.as_str())
 | 
					                    Cookie::build("token", token.as_str())
 | 
				
			||||||
@ -199,7 +205,7 @@ where
 | 
				
			|||||||
                        .finish(),
 | 
					                        .finish(),
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                .cookie(
 | 
					                .cookie(
 | 
				
			||||||
                    Cookie::build("refresh_token", refresh_token + "+" + &request.name)
 | 
					                    Cookie::build("refresh_token", refresh_token + "+" + &name)
 | 
				
			||||||
                        .max_age(max_age.num_days().days())
 | 
					                        .max_age(max_age.num_days().days())
 | 
				
			||||||
                        .path("/auth")
 | 
					                        .path("/auth")
 | 
				
			||||||
                        .http_only(true)
 | 
					                        .http_only(true)
 | 
				
			||||||
@ -211,6 +217,64 @@ where
 | 
				
			|||||||
        .unwrap_or_else(error_to_http_response)
 | 
					        .unwrap_or_else(error_to_http_response)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn opaque_login_finish<Backend>(
 | 
				
			||||||
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
					    request: web::Json<login::ClientLoginFinishRequest>,
 | 
				
			||||||
 | 
					) -> HttpResponse
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Backend: TcpBackendHandler + BackendHandler + OpaqueHandler + 'static,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let name = match data.backend_handler.login_finish(request.clone()).await {
 | 
				
			||||||
 | 
					        Ok(n) => n,
 | 
				
			||||||
 | 
					        Err(e) => return error_to_http_response(e),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    get_login_successful_response(&data, &name).await
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn post_authorize<Backend>(
 | 
				
			||||||
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
					    request: web::Json<BindRequest>,
 | 
				
			||||||
 | 
					) -> HttpResponse
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Backend: TcpBackendHandler + BackendHandler + LoginHandler + 'static,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if let Err(e) = data.backend_handler.bind(request.clone()).await {
 | 
				
			||||||
 | 
					        return error_to_http_response(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    get_login_successful_response(&data, &request.name).await
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn opaque_register_start<Backend>(
 | 
				
			||||||
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
					    request: web::Json<registration::ClientRegistrationStartRequest>,
 | 
				
			||||||
 | 
					) -> ApiResult<registration::ServerRegistrationStartResponse>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Backend: OpaqueHandler + 'static,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    data.backend_handler
 | 
				
			||||||
 | 
					        .registration_start(request.clone())
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					        .map(|res| ApiResult::Left(web::Json(res)))
 | 
				
			||||||
 | 
					        .unwrap_or_else(error_to_api_response)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn opaque_register_finish<Backend>(
 | 
				
			||||||
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
					    request: web::Json<registration::ClientRegistrationFinishRequest>,
 | 
				
			||||||
 | 
					) -> HttpResponse
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    Backend: TcpBackendHandler + BackendHandler + OpaqueHandler + 'static,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if let Err(e) = data
 | 
				
			||||||
 | 
					        .backend_handler
 | 
				
			||||||
 | 
					        .registration_finish(request.clone())
 | 
				
			||||||
 | 
					        .await
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return error_to_http_response(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    HttpResponse::Ok().finish()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct CookieToHeaderTranslatorFactory;
 | 
					pub struct CookieToHeaderTranslatorFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<S> Transform<S, ServiceRequest> for CookieToHeaderTranslatorFactory
 | 
					impl<S> Transform<S, ServiceRequest> for CookieToHeaderTranslatorFactory
 | 
				
			||||||
@ -306,6 +370,22 @@ where
 | 
				
			|||||||
    Backend: TcpBackendHandler + LoginHandler + OpaqueHandler + BackendHandler + 'static,
 | 
					    Backend: TcpBackendHandler + LoginHandler + OpaqueHandler + BackendHandler + 'static,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    cfg.service(web::resource("").route(web::post().to(post_authorize::<Backend>)))
 | 
					    cfg.service(web::resource("").route(web::post().to(post_authorize::<Backend>)))
 | 
				
			||||||
 | 
					        .service(
 | 
				
			||||||
 | 
					            web::resource("/opaque/login/start")
 | 
				
			||||||
 | 
					                .route(web::post().to(opaque_login_start::<Backend>)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .service(
 | 
				
			||||||
 | 
					            web::resource("/opaque/login/finish")
 | 
				
			||||||
 | 
					                .route(web::post().to(opaque_login_finish::<Backend>)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .service(
 | 
				
			||||||
 | 
					            web::resource("/opaque/register/start")
 | 
				
			||||||
 | 
					                .route(web::post().to(opaque_register_start::<Backend>)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .service(
 | 
				
			||||||
 | 
					            web::resource("/opaque/register/finish")
 | 
				
			||||||
 | 
					                .route(web::post().to(opaque_register_finish::<Backend>)),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        .service(web::resource("/refresh").route(web::get().to(get_refresh::<Backend>)))
 | 
					        .service(web::resource("/refresh").route(web::get().to(get_refresh::<Backend>)))
 | 
				
			||||||
        .service(web::resource("/logout").route(web::post().to(post_logout::<Backend>)));
 | 
					        .service(web::resource("/logout").route(web::post().to(post_logout::<Backend>)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,11 +7,11 @@ use crate::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
use actix_web::{web, HttpResponse};
 | 
					use actix_web::{web, HttpResponse};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ApiResult<M> = actix_web::Either<web::Json<M>, HttpResponse>;
 | 
					pub type ApiResult<M> = actix_web::Either<web::Json<M>, HttpResponse>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn user_list_handler<Backend>(
 | 
					async fn user_list_handler<Backend>(
 | 
				
			||||||
    data: web::Data<AppState<Backend>>,
 | 
					    data: web::Data<AppState<Backend>>,
 | 
				
			||||||
 | 
				
			|||||||
@ -73,10 +73,7 @@ fn http_config<Backend>(
 | 
				
			|||||||
    .service(web::scope("/").route("/.*", web::get().to(index)));
 | 
					    .service(web::scope("/").route("/.*", web::get().to(index)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) struct AppState<Backend>
 | 
					pub(crate) struct AppState<Backend> {
 | 
				
			||||||
where
 | 
					 | 
				
			||||||
    Backend: TcpBackendHandler + BackendHandler + 'static,
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    pub backend_handler: Backend,
 | 
					    pub backend_handler: Backend,
 | 
				
			||||||
    pub jwt_key: Hmac<Sha512>,
 | 
					    pub jwt_key: Hmac<Sha512>,
 | 
				
			||||||
    pub jwt_blacklist: RwLock<HashSet<u64>>,
 | 
					    pub jwt_blacklist: RwLock<HashSet<u64>>,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user