use async_graphql::{EmptySubscription, ObjectType, Request, Schema, SubscriptionType};
use std::any::Any;
use crate::auth::core::{get_auth_verdict, get_token_state_from_header, AuthVerdict};
use crate::errors::*;
use crate::graphql::{
get_schema_for_subscriptions, get_schema_without_subscriptions, PublishMutation,
SubscriptionQuery,
};
use crate::options::Options;
#[derive(Clone, Debug)]
pub enum DianaResponse {
Success(String),
Blocked,
Error(String),
}
#[doc(hidden)]
pub enum SysSchema {
WithoutSubscriptions,
ForSubscriptions,
}
#[derive(Clone)]
pub struct DianaHandler<C, Q, M, S>
where
C: Any + Send + Sync + Clone,
Q: Clone + ObjectType + 'static,
M: Clone + ObjectType + 'static,
S: Clone + SubscriptionType + 'static,
{
pub opts: Options<C, Q, M, S>,
pub schema_without_subscriptions: Schema<Q, M, EmptySubscription>,
pub schema_for_subscriptions: Schema<SubscriptionQuery, PublishMutation, S>,
}
impl<C, Q, M, S> DianaHandler<C, Q, M, S>
where
C: Any + Send + Sync + Clone,
Q: Clone + ObjectType + 'static,
M: Clone + ObjectType + 'static,
S: Clone + SubscriptionType + 'static,
{
pub fn new(opts: Options<C, Q, M, S>) -> Result<Self> {
let schema_without_subscriptions = get_schema_without_subscriptions(
opts.schema.clone(),
opts.subscriptions_server_data.clone(),
opts.ctx.clone(),
)?;
let schema_for_subscriptions =
get_schema_for_subscriptions(opts.schema.clone(), opts.ctx.clone());
Ok(DianaHandler {
opts,
schema_without_subscriptions,
schema_for_subscriptions,
})
}
pub fn is_authed<A: Into<String> + std::fmt::Display>(
&self,
raw_auth_header: Option<A>,
) -> AuthVerdict {
let auth_header = raw_auth_header.map(|x| x.to_string());
let auth_header_str = auth_header.as_deref();
let token_state =
get_token_state_from_header(auth_header_str, self.opts.jwt_secret.clone());
get_auth_verdict(token_state, self.opts.authentication_block_state)
}
pub async fn run_stateless_for_subscriptions<A: Into<String> + std::fmt::Display>(
&self,
body: String,
raw_auth_header: Option<A>,
given_auth_verdict: Option<AuthVerdict>,
) -> DianaResponse {
self.run_stateless_req(
SysSchema::ForSubscriptions,
body,
raw_auth_header,
given_auth_verdict,
)
.await
}
pub async fn run_stateless_without_subscriptions<A: Into<String> + std::fmt::Display>(
&self,
body: String,
raw_auth_header: Option<A>,
given_auth_verdict: Option<AuthVerdict>,
) -> DianaResponse {
self.run_stateless_req(
SysSchema::WithoutSubscriptions,
body,
raw_auth_header,
given_auth_verdict,
)
.await
}
#[doc(hidden)]
pub async fn run_stateless_req<A: Into<String> + std::fmt::Display>(
&self,
which_schema: SysSchema,
body: String,
raw_auth_header: Option<A>,
given_auth_verdict: Option<AuthVerdict>,
) -> DianaResponse {
let verdict = match given_auth_verdict {
Some(verdict) => verdict,
None => self.is_authed(raw_auth_header),
};
match verdict {
AuthVerdict::Allow(auth_data) => {
let gql_req = serde_json::from_str::<Request>(&body);
let mut gql_req = match gql_req {
Ok(gql_req) => gql_req,
Err(err) => return DianaResponse::Error(err.to_string()),
};
gql_req = gql_req.data(auth_data);
let res = match which_schema {
SysSchema::WithoutSubscriptions => {
self.schema_without_subscriptions.execute(gql_req).await
}
SysSchema::ForSubscriptions => {
self.schema_for_subscriptions.execute(gql_req).await
}
};
let res_str = serde_json::to_string(&res);
let res_str = match res_str {
Ok(res_str) => res_str,
Err(err) => return DianaResponse::Error(err.to_string()),
};
DianaResponse::Success(res_str)
}
AuthVerdict::Block => DianaResponse::Blocked,
AuthVerdict::Error(err) => DianaResponse::Error(err),
}
}
}