Skip to main content

haste_access_control/
lib.rs

1use crate::context::PermissionLevel;
2use haste_fhir_client::FHIRClient;
3use haste_fhir_model::r4::generated::{
4    resources::AccessPolicyV2,
5    terminology::{AccessPolicyv2Engine, IssueType},
6};
7use haste_fhir_operation_error::OperationOutcomeError;
8use std::sync::Arc;
9
10pub mod context;
11mod engine;
12mod request_reflection;
13mod utilities;
14
15pub async fn evaluate_policy<
16    'a,
17    CTX: Send + Sync + Clone + 'static,
18    Client: FHIRClient<CTX, OperationOutcomeError> + Send + Sync + 'static,
19>(
20    context: Arc<context::PolicyContext<CTX, Client>>,
21    policy: Arc<AccessPolicyV2>,
22) -> Result<PermissionLevel, OperationOutcomeError> {
23    match &*policy.engine {
24        AccessPolicyv2Engine::FullAccess(_) => engine::full_access::evaluate(policy.as_ref()).await,
25        AccessPolicyv2Engine::RuleEngine(_) => {
26            Ok(engine::rule_engine::pdp::evaluate(context, policy).await?)
27        }
28        AccessPolicyv2Engine::Null(_) => Err(OperationOutcomeError::fatal(
29            haste_fhir_model::r4::generated::terminology::IssueType::Forbidden(None),
30            "Access policy denies access.".to_string(),
31        )),
32    }
33}
34
35pub fn evaluate_policies<
36    CTX: Send + Sync + Clone + 'static,
37    Client: FHIRClient<CTX, OperationOutcomeError> + Send + Sync + 'static,
38>(
39    context: context::PolicyContext<CTX, Client>,
40    policies: &Vec<Arc<AccessPolicyV2>>,
41) -> impl Future<Output = Result<context::PolicyContext<CTX, Client>, OperationOutcomeError>> {
42    async move {
43        let mut outcomes = vec![];
44        let context = Arc::new(context);
45
46        for policy in policies {
47            let result = evaluate_policy(context.clone(), policy.clone()).await;
48            if let Ok(permission) = result {
49                match permission {
50                    PermissionLevel::Allow => {
51                        return Arc::into_inner(context).ok_or_else(|| {
52                            OperationOutcomeError::error(
53                                IssueType::Forbidden(None),
54                                "Failed to retrieve policy context.".to_string(),
55                            )
56                        });
57                    }
58                    _ => {}
59                }
60            } else if let Err(e) = result {
61                outcomes.push(e);
62            }
63        }
64
65        Err(OperationOutcomeError::error(
66            IssueType::Forbidden(None),
67            format!("No policy has granted access to your request."),
68        ))
69    }
70}