mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00

This simplifies the flow, and gets rid of wrong clippy warnings about missing awaits due to the instrumentation.
162 lines
5.5 KiB
Rust
162 lines
5.5 KiB
Rust
use crate::{
|
|
domain::{
|
|
error::DomainError,
|
|
handler::{BackendHandler, LoginHandler},
|
|
opaque_handler::OpaqueHandler,
|
|
},
|
|
infra::{
|
|
auth_service,
|
|
configuration::{Configuration, MailOptions},
|
|
logging::CustomRootSpanBuilder,
|
|
tcp_backend_handler::*,
|
|
},
|
|
};
|
|
use actix_files::{Files, NamedFile};
|
|
use actix_http::HttpServiceBuilder;
|
|
use actix_server::ServerBuilder;
|
|
use actix_service::map_config;
|
|
use actix_web::{dev::AppConfig, web, App, HttpResponse};
|
|
use anyhow::{Context, Result};
|
|
use hmac::{Hmac, NewMac};
|
|
use sha2::Sha512;
|
|
use std::collections::HashSet;
|
|
use std::path::PathBuf;
|
|
use std::sync::RwLock;
|
|
use tracing::info;
|
|
|
|
async fn index() -> actix_web::Result<NamedFile> {
|
|
let mut path = PathBuf::new();
|
|
path.push("app");
|
|
path.push("index.html");
|
|
Ok(NamedFile::open(path)?)
|
|
}
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum TcpError {
|
|
#[error("`{0}`")]
|
|
DomainError(#[from] DomainError),
|
|
#[error("Bad request: `{0}`")]
|
|
BadRequest(String),
|
|
#[error("Internal server error: `{0}`")]
|
|
InternalServerError(String),
|
|
#[error("Unauthorized: `{0}`")]
|
|
UnauthorizedError(String),
|
|
}
|
|
|
|
pub type TcpResult<T> = std::result::Result<T, TcpError>;
|
|
|
|
pub(crate) fn error_to_http_response(error: TcpError) -> HttpResponse {
|
|
match error {
|
|
TcpError::DomainError(ref de) => match de {
|
|
DomainError::AuthenticationError(_) | DomainError::AuthenticationProtocolError(_) => {
|
|
HttpResponse::Unauthorized()
|
|
}
|
|
DomainError::DatabaseError(_)
|
|
| DomainError::InternalError(_)
|
|
| DomainError::UnknownCryptoError(_) => HttpResponse::InternalServerError(),
|
|
DomainError::Base64DecodeError(_) | DomainError::BinarySerializationError(_) => {
|
|
HttpResponse::BadRequest()
|
|
}
|
|
},
|
|
TcpError::BadRequest(_) => HttpResponse::BadRequest(),
|
|
TcpError::InternalServerError(_) => HttpResponse::InternalServerError(),
|
|
TcpError::UnauthorizedError(_) => HttpResponse::Unauthorized(),
|
|
}
|
|
.body(error.to_string())
|
|
}
|
|
|
|
fn http_config<Backend>(
|
|
cfg: &mut web::ServiceConfig,
|
|
backend_handler: Backend,
|
|
jwt_secret: secstr::SecUtf8,
|
|
jwt_blacklist: HashSet<u64>,
|
|
server_url: String,
|
|
mail_options: MailOptions,
|
|
) where
|
|
Backend: TcpBackendHandler + BackendHandler + LoginHandler + OpaqueHandler + Sync + 'static,
|
|
{
|
|
cfg.app_data(web::Data::new(AppState::<Backend> {
|
|
backend_handler,
|
|
jwt_key: Hmac::new_varkey(jwt_secret.unsecure().as_bytes()).unwrap(),
|
|
jwt_blacklist: RwLock::new(jwt_blacklist),
|
|
server_url,
|
|
mail_options,
|
|
}))
|
|
.service(web::scope("/auth").configure(auth_service::configure_server::<Backend>))
|
|
// API endpoint.
|
|
.service(
|
|
web::scope("/api")
|
|
.wrap(auth_service::CookieToHeaderTranslatorFactory)
|
|
.configure(super::graphql::api::configure_endpoint::<Backend>),
|
|
)
|
|
// Serve the /pkg path with the compiled WASM app.
|
|
.service(Files::new("/pkg", "./app/pkg"))
|
|
// Serve static files
|
|
.service(Files::new("/static", "./app/static"))
|
|
// Serve static fonts
|
|
.service(Files::new("/static/fonts", "./app/static/fonts"))
|
|
// Default to serve index.html for unknown routes, to support routing.
|
|
.service(
|
|
web::scope("/")
|
|
.route("", web::get().to(index)) // this is necessary because the below doesn't match a request for "/"
|
|
.route(".*", web::get().to(index)),
|
|
);
|
|
}
|
|
|
|
pub(crate) struct AppState<Backend> {
|
|
pub backend_handler: Backend,
|
|
pub jwt_key: Hmac<Sha512>,
|
|
pub jwt_blacklist: RwLock<HashSet<u64>>,
|
|
pub server_url: String,
|
|
pub mail_options: MailOptions,
|
|
}
|
|
|
|
pub async fn build_tcp_server<Backend>(
|
|
config: &Configuration,
|
|
backend_handler: Backend,
|
|
server_builder: ServerBuilder,
|
|
) -> Result<ServerBuilder>
|
|
where
|
|
Backend: TcpBackendHandler + BackendHandler + LoginHandler + OpaqueHandler + Sync + 'static,
|
|
{
|
|
let jwt_secret = config.jwt_secret.clone();
|
|
let jwt_blacklist = backend_handler
|
|
.get_jwt_blacklist()
|
|
.await
|
|
.context("while getting the jwt blacklist")?;
|
|
let server_url = config.http_url.clone();
|
|
let mail_options = config.smtp_options.clone();
|
|
info!("Starting the API/web server on port {}", config.http_port);
|
|
server_builder
|
|
.bind("http", ("0.0.0.0", config.http_port), move || {
|
|
let backend_handler = backend_handler.clone();
|
|
let jwt_secret = jwt_secret.clone();
|
|
let jwt_blacklist = jwt_blacklist.clone();
|
|
let server_url = server_url.clone();
|
|
let mail_options = mail_options.clone();
|
|
HttpServiceBuilder::new()
|
|
.finish(map_config(
|
|
App::new()
|
|
.wrap(tracing_actix_web::TracingLogger::<CustomRootSpanBuilder>::new())
|
|
.configure(move |cfg| {
|
|
http_config(
|
|
cfg,
|
|
backend_handler,
|
|
jwt_secret,
|
|
jwt_blacklist,
|
|
server_url,
|
|
mail_options,
|
|
)
|
|
}),
|
|
|_| AppConfig::default(),
|
|
))
|
|
.tcp()
|
|
})
|
|
.with_context(|| {
|
|
format!(
|
|
"While bringing up the TCP server with port {}",
|
|
config.http_port
|
|
)
|
|
})
|
|
}
|