liminal_server/auth_membership/
connection.rs1use super::{
2 Arc, Decided, LysSession, MembershipAction, MembershipGate, MembershipRefusal,
3 MembershipSubject, bump,
4};
5
6#[derive(Debug, Default)]
13pub struct ConnectionMembership {
14 pub(super) bound: Option<BoundMembership>,
15}
16
17#[derive(Debug)]
18pub(super) struct BoundMembership {
19 pub(super) gate: Arc<MembershipGate>,
20 pub(super) pid: u64,
21 pub(super) holder: Result<MembershipSubject, MembershipRefusal>,
22}
23
24impl ConnectionMembership {
25 #[must_use]
28 pub fn bind(
29 gate: Option<Arc<MembershipGate>>,
30 pid: u64,
31 session: Option<&dyn LysSession>,
32 ) -> Self {
33 Self {
34 bound: gate.map(|gate| BoundMembership {
35 gate,
36 pid,
37 holder: session.map_or(
38 Err(MembershipRefusal::HolderUnverified),
39 MembershipSubject::from_session,
40 ),
41 }),
42 }
43 }
44
45 #[must_use]
47 pub fn gate(&self) -> Option<&Arc<MembershipGate>> {
48 self.bound.as_ref().map(|bound| &bound.gate)
49 }
50
51 pub fn holder(&self) -> Result<&MembershipSubject, MembershipRefusal> {
57 self.bound
58 .as_ref()
59 .ok_or(MembershipRefusal::HolderUnverified)
60 .and_then(|bound| bound.holder.as_ref().map_err(Clone::clone))
61 }
62
63 pub fn admit_connect(&self) -> Result<Option<u64>, MembershipRefusal> {
68 let Some(bound) = &self.bound else {
69 return Ok(None);
70 };
71 if bound.gate.connect_workspace.is_none() {
72 return Ok(None);
73 }
74 let holder = bound.holder.as_ref().map_err(Clone::clone)?;
75 bound.gate.admit_connect(holder)
76 }
77
78 pub fn check(
84 &self,
85 channel: &str,
86 action: MembershipAction,
87 ) -> Result<Decided, MembershipRefusal> {
88 self.decide(channel, action, false)
89 }
90
91 pub fn release(
96 &self,
97 channel: &str,
98 action: MembershipAction,
99 ) -> Result<Decided, MembershipRefusal> {
100 self.decide(channel, action, true)
101 }
102
103 pub fn release_conversation(&self, conversation: u64) -> Result<Decided, MembershipRefusal> {
109 let Some(bound) = &self.bound else {
110 return Ok(Decided::NotProtected);
111 };
112 let Some(channel) = bound.gate.channel_for_conversation(conversation) else {
113 return Ok(Decided::NotProtected);
114 };
115 self.decide(channel, MembershipAction::Read, true)
116 }
117
118 pub(super) fn decide(
119 &self,
120 channel: &str,
121 action: MembershipAction,
122 release: bool,
123 ) -> Result<Decided, MembershipRefusal> {
124 let Some(bound) = &self.bound else {
125 return Ok(Decided::NotProtected);
126 };
127 if !bound.gate.protects(channel) {
128 return Ok(Decided::NotProtected);
129 }
130 let holder = match &bound.holder {
131 Ok(holder) => holder,
132 Err(refusal) => {
133 bump(&bound.gate.counters.refused);
134 return Err(refusal.clone());
135 }
136 };
137 if release {
138 bound
139 .gate
140 .check_release(bound.pid, Some(holder), channel, action)
141 } else {
142 bound.gate.check(bound.pid, Some(holder), channel, action)
143 }
144 }
145}
146
147impl ConnectionMembership {
148 #[must_use]
153 pub fn release_check(&self, channel: &str, action: MembershipAction) -> Option<ReleaseCheck> {
154 let bound = self.bound.as_ref()?;
155 if !bound.gate.protects(channel) {
156 return None;
157 }
158 let holder = bound.holder.as_ref().ok()?;
159 Some(ReleaseCheck {
160 gate: Arc::clone(&bound.gate),
161 pid: bound.pid,
162 holder: holder.clone(),
163 channel: channel.to_owned(),
164 action,
165 })
166 }
167}
168
169#[derive(Clone, Debug)]
176pub struct ReleaseCheck {
177 pub(super) gate: Arc<MembershipGate>,
178 pub(super) pid: u64,
179 pub(super) holder: MembershipSubject,
180 pub(super) channel: String,
181 pub(super) action: MembershipAction,
182}
183
184impl ReleaseCheck {
185 pub fn check(&self) -> Result<Decided, MembershipRefusal> {
190 self.gate
191 .check_release(self.pid, Some(&self.holder), &self.channel, self.action)
192 }
193}
194
195impl Drop for ConnectionMembership {
196 fn drop(&mut self) {
197 if let Some(bound) = &self.bound {
198 bound.gate.release_connection(bound.pid);
199 }
200 }
201}