use apiplant_abi::{FunctionAccess, FunctionPolicy};
use apiplant_auth::Principal;
use ntex::web::types::State;
use ntex::web::{HttpRequest, HttpResponse};
use crate::response::error;
use crate::state::AppState;
pub async fn check(
state: &State<AppState>,
req: &HttpRequest,
policy: &FunctionPolicy,
missing: &str,
) -> Result<Option<Principal>, HttpResponse> {
let principal = state.resolve_principal(req).await;
let access = &policy.access;
if let Some(class) = policy.org_class.as_deref() {
if !matches!(access, FunctionAccess::Private) {
let membership = principal.as_ref().and_then(|p| {
state
.active_org(req, &principal)
.and_then(|org| p.membership(org))
});
match membership {
Some(m) if m.is_class(class) => {}
Some(_) => {
return Err(error(
403,
format!("requires an organisation of class `{class}`"),
))
}
None if principal.is_some() => {
return Err(error(
403,
"select an organisation with the X-Organization header",
))
}
None => return Err(error(401, "authentication required")),
}
}
}
match access {
FunctionAccess::Public => {}
FunctionAccess::Private => return Err(error(404, missing.to_string())),
FunctionAccess::Authenticated => {
if principal.is_none() {
return Err(error(401, "authentication required"));
}
}
FunctionAccess::Member | FunctionAccess::Role(_) => {
if principal.is_none() {
return Err(error(401, "authentication required"));
}
let org = state.active_org(req, &principal);
let ok = match (access, org, principal.as_ref()) {
(FunctionAccess::Member, Some(org), Some(caller)) => caller.is_member(org),
(FunctionAccess::Role(required), Some(org), Some(caller)) => {
caller.has_role_in(org, required)
}
_ => false,
};
if !ok {
return Err(error(403, "forbidden"));
}
}
}
Ok(principal)
}