mail4agent_server/
policy.rs1use std::collections::BTreeMap;
7
8use crate::store::RoomKind;
9
10pub type Claims = BTreeMap<String, String>;
12
13pub fn claims_from(paid_flag: u8) -> Claims {
15 let mut c = Claims::new();
16 c.insert("flag".into(), paid_flag.to_string());
17 c
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum Action {
23 CreateRoom,
24 Invite,
25 JoinRoom,
26 SendEvent,
27 UploadMedia,
28}
29
30#[derive(Debug, Clone)]
31pub struct PolicyContext<'a> {
32 pub claims: &'a Claims,
33 pub action: Action,
34 pub room_kind: Option<RoomKind>,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum Decision {
40 Allow,
41 Deny(String),
43}
44
45pub trait PolicyHook: Send + Sync {
46 fn decide(&self, ctx: &PolicyContext<'_>) -> Decision;
47}
48
49pub struct AllowAll;
51
52impl PolicyHook for AllowAll {
53 fn decide(&self, _ctx: &PolicyContext<'_>) -> Decision {
54 Decision::Allow
55 }
56}
57
58pub fn enforce(hook: &dyn PolicyHook, claims: &Claims, action: Action, room_kind: Option<RoomKind>) -> Result<(), crate::error::MatrixError> {
60 match hook.decide(&PolicyContext { claims, action, room_kind }) {
61 Decision::Allow => Ok(()),
62 Decision::Deny(why) => Err(crate::error::MatrixError::policy_denied(why)),
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 struct DenyFlaglessChannels;
71 impl PolicyHook for DenyFlaglessChannels {
72 fn decide(&self, c: &PolicyContext<'_>) -> Decision {
73 if c.action == Action::CreateRoom && c.room_kind == Some(RoomKind::Channel) && c.claims.get("flag").map(String::as_str) != Some("1") {
74 Decision::Deny("not available".into())
75 } else {
76 Decision::Allow
77 }
78 }
79 }
80
81 #[test]
82 fn default_allows_and_custom_hook_denies_with_policy_error() {
83 let c0 = claims_from(0);
84 assert!(enforce(&AllowAll, &c0, Action::CreateRoom, Some(RoomKind::Channel)).is_ok());
85 let e = enforce(&DenyFlaglessChannels, &c0, Action::CreateRoom, Some(RoomKind::Channel)).unwrap_err();
86 assert_eq!((e.status, e.errcode), (403, "M4A_POLICY_DENIED"));
87 assert!(enforce(&DenyFlaglessChannels, &c0, Action::CreateRoom, Some(RoomKind::Dm)).is_ok());
88 assert!(enforce(&DenyFlaglessChannels, &claims_from(1), Action::CreateRoom, Some(RoomKind::Channel)).is_ok());
89 }
90}