Skip to main content

solid_pod_rs/wac/
issuer.rs

1//! `acl:IssuerCondition` — gate authorisation on the token issuer.
2//!
3//! An `IssuerCondition` is satisfied when the request context's
4//! `issuer` (typically the `iss` claim from an OIDC/DPoP access token)
5//! matches a listed `acl:issuer` IRI, belongs to a listed
6//! `acl:issuerGroup`, or the class predicate names a catch-all.
7
8use serde::{Deserialize, Serialize};
9
10use crate::wac::conditions::{ConditionOutcome, RequestContext};
11use crate::wac::document::{get_ids, IdOrIds};
12use crate::wac::evaluator::GroupMembership;
13
14/// Body of an `acl:IssuerCondition`.
15#[derive(Debug, Clone, Default, Deserialize, Serialize)]
16pub struct IssuerConditionBody {
17    #[serde(rename = "acl:issuer", default, skip_serializing_if = "Option::is_none")]
18    pub issuer: Option<IdOrIds>,
19
20    #[serde(
21        rename = "acl:issuerGroup",
22        default,
23        skip_serializing_if = "Option::is_none"
24    )]
25    pub issuer_group: Option<IdOrIds>,
26
27    #[serde(
28        rename = "acl:issuerClass",
29        default,
30        skip_serializing_if = "Option::is_none"
31    )]
32    pub issuer_class: Option<IdOrIds>,
33}
34
35/// Default evaluator for `acl:IssuerCondition`.
36#[derive(Debug, Default, Clone, Copy)]
37pub struct IssuerConditionEvaluator;
38
39impl IssuerConditionEvaluator {
40    pub fn evaluate(
41        &self,
42        body: &IssuerConditionBody,
43        ctx: &RequestContext<'_>,
44        groups: &dyn GroupMembership,
45    ) -> ConditionOutcome {
46        // Catch-all class (rarely useful but specified for completeness).
47        for cls in get_ids(&body.issuer_class) {
48            if cls == "foaf:Agent" || cls == "http://xmlns.com/foaf/0.1/Agent" {
49                return ConditionOutcome::Satisfied;
50            }
51        }
52
53        let Some(iss) = ctx.issuer else {
54            // No issuer in context — with no class allowlist, condition
55            // is definitively denied.
56            return ConditionOutcome::Denied;
57        };
58
59        for i in get_ids(&body.issuer) {
60            if i == iss {
61                return ConditionOutcome::Satisfied;
62            }
63        }
64        for g in get_ids(&body.issuer_group) {
65            if groups.is_member(g, iss) {
66                return ConditionOutcome::Satisfied;
67            }
68        }
69
70        ConditionOutcome::Denied
71    }
72}