server: update actix, inline juniper-actix

This commit is contained in:
Valentin Tolmer 2023-02-23 16:48:37 +01:00 committed by nitnelave
parent 07de6062ca
commit dce73f91ef
6 changed files with 820 additions and 949 deletions

1595
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,3 +11,6 @@ default-members = ["server"]
[patch.crates-io.opaque-ke] [patch.crates-io.opaque-ke]
git = 'https://github.com/nitnelave/opaque-ke/' git = 'https://github.com/nitnelave/opaque-ke/'
branch = 'zeroize_1.5' branch = 'zeroize_1.5'
[patch.crates-io.lber]
git = 'https://github.com/inejge/ldap3/'

View File

@ -5,14 +5,14 @@ name = "lldap"
version = "0.4.2-alpha" version = "0.4.2-alpha"
[dependencies] [dependencies]
actix = "0.12" actix = "0.13"
actix-files = "0.6.0-beta.6" actix-files = "0.6"
actix-http = "=3.0.0-beta.9" actix-http = "3"
actix-rt = "2.2.0" actix-rt = "2"
actix-server = "=2.0.0-beta.5" actix-server = "2"
actix-service = "2.0.0" actix-service = "2"
actix-web = "=4.0.0-beta.8" actix-web = "4.3"
actix-web-httpauth = "0.6.0-beta.2" actix-web-httpauth = "0.8"
anyhow = "*" anyhow = "*"
async-trait = "0.1" async-trait = "0.1"
base64 = "0.13" base64 = "0.13"
@ -25,9 +25,9 @@ futures-util = "*"
hmac = "0.10" hmac = "0.10"
http = "*" http = "*"
itertools = "0.10.1" itertools = "0.10.1"
juniper = "0.15.10" juniper = "0.15"
juniper_actix = "0.4.0" jwt = "0.16"
jwt = "0.13" lber = "0.4.1"
ldap3_proto = ">=0.3.1" ldap3_proto = ">=0.3.1"
log = "*" log = "*"
orion = "0.16" orion = "0.16"
@ -36,12 +36,12 @@ serde = "*"
serde_json = "1" serde_json = "1"
sha2 = "0.9" sha2 = "0.9"
thiserror = "*" thiserror = "*"
time = "0.2" time = "0.3"
tokio-rustls = "0.23" tokio-rustls = "0.23"
tokio-stream = "*" tokio-stream = "*"
tokio-util = "0.7.3" tokio-util = "0.7.3"
tracing = "*" tracing = "*"
tracing-actix-web = "0.4.0-beta.7" tracing-actix-web = "0.7"
tracing-attributes = "^0.1.21" tracing-attributes = "^0.1.21"
tracing-log = "*" tracing-log = "*"
rustls-pemfile = "1.0.0" rustls-pemfile = "1.0.0"
@ -97,7 +97,7 @@ version = "^0.1.4"
[dependencies.actix-tls] [dependencies.actix-tls]
features = ["default", "rustls"] features = ["default", "rustls"]
version = "=3.0.0-beta.5" version = "3"
[dependencies.image] [dependencies.image]
features = ["jpeg"] features = ["jpeg"]

View File

@ -451,14 +451,15 @@ where
#[instrument(skip_all, level = "debug")] #[instrument(skip_all, level = "debug")]
async fn opaque_register_start<Backend>( async fn opaque_register_start<Backend>(
request: actix_web::HttpRequest, request: actix_web::HttpRequest,
mut payload: actix_web::web::Payload, payload: actix_web::web::Payload,
data: web::Data<AppState<Backend>>, data: web::Data<AppState<Backend>>,
) -> TcpResult<registration::ServerRegistrationStartResponse> ) -> TcpResult<registration::ServerRegistrationStartResponse>
where where
Backend: BackendHandler + OpaqueHandler + 'static, Backend: BackendHandler + OpaqueHandler + 'static,
{ {
use actix_web::FromRequest; use actix_web::FromRequest;
let validation_result = BearerAuth::from_request(&request, &mut payload.0) let inner_payload = &mut payload.into_inner();
let validation_result = BearerAuth::from_request(&request, inner_payload)
.await .await
.ok() .ok()
.and_then(|bearer| check_if_token_is_valid(&data, bearer.token()).ok()) .and_then(|bearer| check_if_token_is_valid(&data, bearer.token()).ok())
@ -468,7 +469,7 @@ where
let registration_start_request = let registration_start_request =
web::Json::<registration::ClientRegistrationStartRequest>::from_request( web::Json::<registration::ClientRegistrationStartRequest>::from_request(
&request, &request,
&mut payload.0, inner_payload,
) )
.await .await
.map_err(|e| TcpError::BadRequest(format!("{:#?}", e)))? .map_err(|e| TcpError::BadRequest(format!("{:#?}", e)))?

View File

@ -11,10 +11,17 @@ use crate::{
tcp_server::AppState, tcp_server::AppState,
}, },
}; };
use actix_web::{web, Error, HttpResponse}; use actix_web::FromRequest;
use actix_web::HttpMessage;
use actix_web::{error::JsonPayloadError, web, Error, HttpRequest, HttpResponse};
use actix_web_httpauth::extractors::bearer::BearerAuth; use actix_web_httpauth::extractors::bearer::BearerAuth;
use juniper::{EmptySubscription, FieldError, RootNode}; use juniper::{
use juniper_actix::{graphiql_handler, graphql_handler, playground_handler}; http::{
graphiql::graphiql_source, playground::playground_source, GraphQLBatchRequest,
GraphQLRequest,
},
EmptySubscription, FieldError, RootNode, ScalarValue,
};
use tracing::debug; use tracing::debug;
pub struct Context<Handler: BackendHandler> { pub struct Context<Handler: BackendHandler> {
@ -100,25 +107,129 @@ pub fn export_schema(opts: ExportGraphQLSchemaOpts) -> anyhow::Result<()> {
} }
async fn graphiql_route() -> Result<HttpResponse, Error> { async fn graphiql_route() -> Result<HttpResponse, Error> {
graphiql_handler("/api/graphql", None).await let html = graphiql_source("/api/graphql", None);
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html))
} }
async fn playground_route() -> Result<HttpResponse, Error> { async fn playground_route() -> Result<HttpResponse, Error> {
playground_handler("/api/graphql", None).await let html = playground_source("/api/graphql", None);
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html))
}
#[derive(serde::Deserialize, Clone, PartialEq, Debug)]
struct GetGraphQLRequest {
query: String,
#[serde(rename = "operationName")]
operation_name: Option<String>,
variables: Option<String>,
}
impl<S> From<GetGraphQLRequest> for GraphQLRequest<S>
where
S: ScalarValue,
{
fn from(get_req: GetGraphQLRequest) -> Self {
let GetGraphQLRequest {
query,
operation_name,
variables,
} = get_req;
let variables = variables.map(|s| serde_json::from_str(&s).unwrap());
Self::new(query, operation_name, variables)
}
}
/// Actix GraphQL Handler for GET requests
pub async fn get_graphql_handler<Query, Mutation, Subscription, CtxT, S>(
schema: &juniper::RootNode<'static, Query, Mutation, Subscription, S>,
context: &CtxT,
req: HttpRequest,
) -> Result<HttpResponse, Error>
where
Query: juniper::GraphQLTypeAsync<S, Context = CtxT>,
Query::TypeInfo: Sync,
Mutation: juniper::GraphQLTypeAsync<S, Context = CtxT>,
Mutation::TypeInfo: Sync,
Subscription: juniper::GraphQLSubscriptionType<S, Context = CtxT>,
Subscription::TypeInfo: Sync,
CtxT: Sync,
S: ScalarValue + Send + Sync,
{
let get_req = web::Query::<GetGraphQLRequest>::from_query(req.query_string())?;
let req = GraphQLRequest::from(get_req.into_inner());
let gql_response = req.execute(schema, context).await;
let body_response = serde_json::to_string(&gql_response)?;
let mut response = match gql_response.is_ok() {
true => HttpResponse::Ok(),
false => HttpResponse::BadRequest(),
};
Ok(response
.content_type("application/json")
.body(body_response))
}
/// Actix GraphQL Handler for POST requests
pub async fn post_graphql_handler<Query, Mutation, Subscription, CtxT, S>(
schema: &juniper::RootNode<'static, Query, Mutation, Subscription, S>,
context: &CtxT,
req: HttpRequest,
mut payload: actix_http::Payload,
) -> Result<HttpResponse, Error>
where
Query: juniper::GraphQLTypeAsync<S, Context = CtxT>,
Query::TypeInfo: Sync,
Mutation: juniper::GraphQLTypeAsync<S, Context = CtxT>,
Mutation::TypeInfo: Sync,
Subscription: juniper::GraphQLSubscriptionType<S, Context = CtxT>,
Subscription::TypeInfo: Sync,
CtxT: Sync,
S: ScalarValue + Send + Sync,
{
let req = match req.content_type() {
"application/json" => {
let body = String::from_request(&req, &mut payload).await?;
serde_json::from_str::<GraphQLBatchRequest<S>>(&body)
.map_err(JsonPayloadError::Deserialize)
}
"application/graphql" => {
let body = String::from_request(&req, &mut payload).await?;
Ok(GraphQLBatchRequest::Single(GraphQLRequest::new(
body, None, None,
)))
}
_ => Err(JsonPayloadError::ContentType),
}?;
let gql_batch_response = req.execute(schema, context).await;
let gql_response = serde_json::to_string(&gql_batch_response)?;
let mut response = match gql_batch_response.is_ok() {
true => HttpResponse::Ok(),
false => HttpResponse::BadRequest(),
};
Ok(response.content_type("application/json").body(gql_response))
} }
async fn graphql_route<Handler: BackendHandler + Clone>( async fn graphql_route<Handler: BackendHandler + Clone>(
req: actix_web::HttpRequest, req: actix_web::HttpRequest,
mut payload: actix_web::web::Payload, payload: actix_web::web::Payload,
data: web::Data<AppState<Handler>>, data: web::Data<AppState<Handler>>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
use actix_web::FromRequest; let mut inner_payload = payload.into_inner();
let bearer = BearerAuth::from_request(&req, &mut payload.0).await?; let bearer = BearerAuth::from_request(&req, &mut inner_payload).await?;
let validation_result = check_if_token_is_valid(&data, bearer.token())?; let validation_result = check_if_token_is_valid(&data, bearer.token())?;
let context = Context::<Handler> { let context = Context::<Handler> {
handler: data.backend_handler.clone(), handler: data.backend_handler.clone(),
validation_result, validation_result,
}; };
graphql_handler(&schema(), &context, req, payload).await let schema = &schema();
let context = &context;
match *req.method() {
actix_http::Method::POST => post_graphql_handler(schema, context, req, inner_payload).await,
actix_http::Method::GET => get_graphql_handler(schema, context, req).await,
_ => Err(actix_web::error::UrlGenerationError::ResourceNotFound.into()),
}
} }
pub fn configure_endpoint<Backend>(cfg: &mut web::ServiceConfig) pub fn configure_endpoint<Backend>(cfg: &mut web::ServiceConfig)

View File

@ -85,7 +85,10 @@ fn http_config<Backend>(
server_url, server_url,
mail_options, mail_options,
})) }))
.route("/health", web::get().to(|| HttpResponse::Ok().finish())) .route(
"/health",
web::get().to(|| async { HttpResponse::Ok().finish() }),
)
.service( .service(
web::scope("/auth") web::scope("/auth")
.configure(|cfg| auth_service::configure_server::<Backend>(cfg, enable_password_reset)), .configure(|cfg| auth_service::configure_server::<Backend>(cfg, enable_password_reset)),
@ -165,7 +168,7 @@ where
let jwt_blacklist = jwt_blacklist.clone(); let jwt_blacklist = jwt_blacklist.clone();
let server_url = server_url.clone(); let server_url = server_url.clone();
let mail_options = mail_options.clone(); let mail_options = mail_options.clone();
HttpServiceBuilder::new() HttpServiceBuilder::default()
.finish(map_config( .finish(map_config(
App::new() App::new()
.wrap(tracing_actix_web::TracingLogger::<CustomRootSpanBuilder>::new()) .wrap(tracing_actix_web::TracingLogger::<CustomRootSpanBuilder>::new())