1use crate::indexing::QuinIndex;
24use crate::modalities::interaction_governance::{
25 map_policy, permits_execution, Governance, PolicyMode,
26};
27use crate::modalities::logic::deontic::DeonticStatus;
28
29#[derive(Debug, Clone, Copy)]
32pub struct CallerStandpoint {
33 pub agent: u64,
35 pub role: u64,
37 pub verified: bool,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum CooperationVerdict {
44 Authorized(PolicyMode),
46 DeniedUnverified,
48 DeniedUngrounded,
50 DeniedByPolicy(PolicyMode),
53}
54
55#[inline]
58pub fn caller_grounded(index: &QuinIndex, agent: u64) -> bool {
59 !crate::agent::is_ungrounded_agency(index, agent)
60}
61
62pub fn authorize(
65 standpoint: &CallerStandpoint,
66 grounded: bool,
67 request_status: DeonticStatus,
68 governance: Governance,
69) -> CooperationVerdict {
70 if !standpoint.verified {
71 return CooperationVerdict::DeniedUnverified;
72 }
73 if !grounded {
74 return CooperationVerdict::DeniedUngrounded;
75 }
76 let mode = map_policy(request_status, governance);
77 if permits_execution(mode) {
78 CooperationVerdict::Authorized(mode)
79 } else {
80 CooperationVerdict::DeniedByPolicy(mode)
81 }
82}
83
84pub fn authorize_call(
87 index: &QuinIndex,
88 standpoint: &CallerStandpoint,
89 request_status: DeonticStatus,
90 governance: Governance,
91) -> CooperationVerdict {
92 authorize(
93 standpoint,
94 caller_grounded(index, standpoint.agent),
95 request_status,
96 governance,
97 )
98}
99
100pub fn enforcement_enabled() -> bool {
105 matches!(
106 std::env::var("QUALIA_MCP_ENFORCE").ok().as_deref(),
107 Some("1") | Some("true") | Some("TRUE") | Some("on")
108 )
109}
110
111pub const fn cooperation_label(v: CooperationVerdict) -> &'static str {
113 match v {
114 CooperationVerdict::Authorized(_) => "Authorized",
115 CooperationVerdict::DeniedUnverified => "DeniedUnverified",
116 CooperationVerdict::DeniedUngrounded => "DeniedUngrounded",
117 CooperationVerdict::DeniedByPolicy(_) => "DeniedByPolicy",
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::agent::{A_ARTIFICIAL_AGENT, A_NATURAL_PERSON, P_OPERATED_BY, P_RDF_TYPE};
125 use crate::q_hash;
126 use crate::NQuin;
127
128 fn sp(agent: u64, verified: bool) -> CallerStandpoint {
129 CallerStandpoint {
130 agent,
131 role: q_hash("role:requester"),
132 verified,
133 }
134 }
135 fn t(s: u64, p: u64, o: u64) -> NQuin {
136 let mut q = NQuin {
137 subject: s,
138 predicate: p,
139 object: o,
140 context: 0,
141 metadata: 0,
142 parity: 0,
143 };
144 q.parity = q.subject ^ q.predicate ^ q.object ^ q.context;
145 q
146 }
147
148 #[test]
149 fn unverified_caller_is_denied() {
150 let v = authorize(
151 &sp(q_hash("did:x"), false),
152 true,
153 DeonticStatus::Active,
154 Governance::default(),
155 );
156 assert_eq!(v, CooperationVerdict::DeniedUnverified);
157 }
158
159 #[test]
160 fn ungrounded_caller_is_denied() {
161 let v = authorize(
162 &sp(q_hash("did:bot"), true),
163 false,
164 DeonticStatus::Active,
165 Governance::default(),
166 );
167 assert_eq!(v, CooperationVerdict::DeniedUngrounded);
168 }
169
170 #[test]
171 fn verified_grounded_ordinary_call_is_authorized() {
172 let v = authorize(
173 &sp(q_hash("did:alice"), true),
174 true,
175 DeonticStatus::Active,
176 Governance::default(),
177 );
178 assert_eq!(v, CooperationVerdict::Authorized(PolicyMode::Allow));
179 }
180
181 #[test]
182 fn non_derogable_violation_request_is_blocked_by_policy() {
183 let g = Governance {
184 non_derogable: true,
185 humanitarian: false,
186 ambiguous: false,
187 };
188 let v = authorize(
189 &sp(q_hash("did:alice"), true),
190 true,
191 DeonticStatus::Violated,
192 g,
193 );
194 assert_eq!(
195 v,
196 CooperationVerdict::DeniedByPolicy(PolicyMode::PreventiveBlock)
197 );
198 }
199
200 #[test]
201 fn authorize_call_resolves_grounding_from_the_graph() {
202 let bot = q_hash("did:bot");
204 let idx = QuinIndex::from_slice(&[t(bot, P_RDF_TYPE, A_ARTIFICIAL_AGENT)]);
205 assert_eq!(
206 authorize_call(
207 &idx,
208 &sp(bot, true),
209 DeonticStatus::Active,
210 Governance::default()
211 ),
212 CooperationVerdict::DeniedUngrounded
213 );
214 let human = q_hash("did:alice");
216 let idx2 = QuinIndex::from_slice(&[
217 t(bot, P_RDF_TYPE, A_ARTIFICIAL_AGENT),
218 t(bot, P_OPERATED_BY, human),
219 ]);
220 assert_eq!(
221 authorize_call(
222 &idx2,
223 &sp(bot, true),
224 DeonticStatus::Active,
225 Governance::default()
226 ),
227 CooperationVerdict::Authorized(PolicyMode::Allow)
228 );
229 let alice = q_hash("did:alice");
231 let idx3 = QuinIndex::from_slice(&[t(alice, P_RDF_TYPE, A_NATURAL_PERSON)]);
232 assert!(matches!(
233 authorize_call(
234 &idx3,
235 &sp(alice, true),
236 DeonticStatus::Active,
237 Governance::default()
238 ),
239 CooperationVerdict::Authorized(_)
240 ));
241 }
242}