use std::sync::Arc;
use nest_rs_core::{HandlerMetadata, Layer, injectable};
use nest_rs_graphql::async_graphql::Context as GraphqlContext;
use nest_rs_guards::{Denial, Guard};
use nest_rs_http::{Reflector, async_trait};
use nest_rs_ws::WsClient;
use poem::Request;
use serde_json::Value;
use crate::{AbilityBuilder, AbilityFactory, current_ability};
#[injectable]
pub struct AbilityGuard<F: AbilityFactory> {
#[inject]
factory: Arc<F>,
}
impl<F: AbilityFactory> Layer for AbilityGuard<F> {}
#[async_trait]
impl<F: AbilityFactory> Guard for AbilityGuard<F> {
async fn check_http(&self, req: &mut Request) -> Result<(), Denial> {
match req.extensions().get::<F::Actor>().cloned() {
Some(actor) => {
let mut builder = AbilityBuilder::new();
self.factory.define(&actor, &mut builder);
req.extensions_mut().insert(Arc::new(builder.build()));
Ok(())
}
None if Reflector::new(req).is_public() => {
req.extensions_mut()
.insert(Arc::new(AbilityBuilder::new().build()));
Ok(())
}
None => Err(Denial::internal(
"AbilityGuard requires an authentication guard to run first",
)),
}
}
async fn check_graphql(&self, _ctx: &GraphqlContext<'_>) -> Result<(), Denial> {
if current_ability().is_none() {
return Err(Denial::unauthorized(
"no ambient ability — authentication did not run on the GraphQL operation",
));
}
Ok(())
}
async fn check_ws_message(
&self,
_client: &WsClient,
_event: &str,
_data: &Value,
) -> Result<(), Denial> {
if current_ability().is_none() {
return Err(Denial::unauthorized(
"no ambient ability — WS connection did not authenticate",
));
}
Ok(())
}
}