openclaw_channels/
allowlist.rs1use serde::{Deserialize, Serialize};
4
5use openclaw_core::types::{ChannelId, PeerId};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct AllowlistEntry {
10 pub channel: String,
12 pub peer_id: String,
14 pub label: Option<String>,
16}
17
18impl AllowlistEntry {
19 #[must_use]
21 pub fn new(channel: impl Into<String>, peer_id: impl Into<String>) -> Self {
22 Self {
23 channel: channel.into(),
24 peer_id: peer_id.into(),
25 label: None,
26 }
27 }
28
29 #[must_use]
31 pub fn channel_wide(channel: impl Into<String>) -> Self {
32 Self::new(channel, "*")
33 }
34
35 #[must_use]
37 pub fn peer_anywhere(peer_id: impl Into<String>) -> Self {
38 Self::new("*", peer_id)
39 }
40
41 #[must_use]
43 pub fn matches(&self, channel: &ChannelId, peer_id: &PeerId) -> bool {
44 let channel_matches = self.channel == "*" || self.channel == channel.as_ref();
45 let peer_matches = self.peer_id == "*" || self.peer_id == peer_id.as_ref();
46 channel_matches && peer_matches
47 }
48}
49
50#[derive(Debug, Clone, Default)]
52pub struct Allowlist {
53 entries: Vec<AllowlistEntry>,
54 default_allow: bool,
55}
56
57impl Allowlist {
58 #[must_use]
60 pub const fn new() -> Self {
61 Self {
62 entries: Vec::new(),
63 default_allow: false,
64 }
65 }
66
67 #[must_use]
69 pub const fn open() -> Self {
70 Self {
71 entries: Vec::new(),
72 default_allow: true,
73 }
74 }
75
76 pub fn add(&mut self, entry: AllowlistEntry) {
78 self.entries.push(entry);
79 }
80
81 #[must_use]
83 pub fn is_allowed(&self, channel: &ChannelId, peer_id: &PeerId) -> bool {
84 if self.entries.is_empty() {
85 return self.default_allow;
86 }
87
88 self.entries.iter().any(|e| e.matches(channel, peer_id))
89 }
90
91 #[must_use]
93 pub fn entries(&self) -> &[AllowlistEntry] {
94 &self.entries
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_allowlist_empty_deny() {
104 let allowlist = Allowlist::new();
105 assert!(!allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("123")));
106 }
107
108 #[test]
109 fn test_allowlist_empty_allow() {
110 let allowlist = Allowlist::open();
111 assert!(allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("123")));
112 }
113
114 #[test]
115 fn test_allowlist_specific() {
116 let mut allowlist = Allowlist::new();
117 allowlist.add(AllowlistEntry::new("telegram", "123"));
118
119 assert!(allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("123")));
120 assert!(!allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("456")));
121 assert!(!allowlist.is_allowed(&ChannelId::discord(), &PeerId::new("123")));
122 }
123
124 #[test]
125 fn test_allowlist_wildcard() {
126 let mut allowlist = Allowlist::new();
127 allowlist.add(AllowlistEntry::channel_wide("telegram"));
128
129 assert!(allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("123")));
130 assert!(allowlist.is_allowed(&ChannelId::telegram(), &PeerId::new("456")));
131 assert!(!allowlist.is_allowed(&ChannelId::discord(), &PeerId::new("123")));
132 }
133}