bacnet_server/server/
local_authorization.rs1use super::cov_fanout::CovFanout;
4use super::local_writes::local_runtime;
5use super::*;
6use bacnet_objects::access_control::{
7 AccessEventReport, AuthorizationEvaluation, AuthorizationOutcome, AuthorizationRequest,
8 evaluate_authorization,
9};
10use bacnet_types::enums::Reliability;
11use tokio::sync::{OwnedRwLockReadGuard, OwnedRwLockWriteGuard};
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct LocalAuthorizationResult {
16 pub evaluation: AuthorizationEvaluation,
18 pub committed_events: Vec<AccessEventReport>,
21}
22
23impl<T: TransportPort + 'static> BACnetServer<T> {
24 pub async fn authorize_access_local(
50 &self,
51 request: AuthorizationRequest<'_>,
52 tag: u64,
53 ) -> Result<LocalAuthorizationResult, Error> {
54 let runtime = local_runtime()?;
55 self.active_network()?;
56 let oid = request.original.point;
57 let (mut db, table) = self.authorization_guards().await;
58 admit(&db, &oid)?;
59 let evaluation = evaluate_authorization(&db, request)?;
60 if !matches!(
61 evaluation.outcome,
62 AuthorizationOutcome::Granted | AuthorizationOutcome::Denied { .. }
63 ) {
64 return Ok(LocalAuthorizationResult {
65 evaluation,
66 committed_events: Vec::new(),
67 });
68 }
69 let reports: Vec<_> = evaluation
70 .events
71 .iter()
72 .map(|intent| AccessEventReport {
73 credential: Some(intent.presentation.credential.into()),
74 ..AccessEventReport::new(intent.access_event, tag)
75 })
76 .collect();
77 let committed = db
78 .get_mut(&oid)
79 .expect("admitted point remains under the same database guard")
80 .record_access_event_batch_internal(&reports)?;
81 let records = committed.iter().map(|event| event.report.clone()).collect();
82 for event in &committed {
83 table
84 .timed_capture(oid)
85 .run_from_snapshot(event.snapshot.as_ref(), event.clock_frame);
86 }
87 drop(table);
91 let fanout = CovFanout::new(&self.local_writer().cov_context(), &self.event_suppressions);
92 let routers = Arc::clone(&self.learned_routers);
93 let bindings = Arc::clone(&self.device_bindings);
94 self.finish_in_task(&runtime, async move {
95 drop(db);
96 Self::fire_event_notifications_with_bindings(
97 &fanout.event_delivery(&routers, &bindings),
98 &fanout.cov_table,
99 &oid,
100 )
101 .await;
102 for event in committed {
103 Self::fire_cov_notifications_from_snapshot(
104 &fanout.notify_context(),
105 &oid,
106 event.snapshot.as_ref(),
107 )
108 .await;
109 }
110 })
111 .await?;
112 Ok(LocalAuthorizationResult {
113 evaluation,
114 committed_events: records,
115 })
116 }
117
118 async fn authorization_guards(
121 &self,
122 ) -> (
123 OwnedRwLockWriteGuard<ObjectDatabase>,
124 OwnedRwLockReadGuard<CovSubscriptionTable>,
125 ) {
126 loop {
127 let db = Arc::clone(&self.db).write_owned().await;
128 if let Ok(table) = Arc::clone(&self.cov_table).try_read_owned() {
129 return (db, table);
130 }
131 drop(db);
132 drop(self.cov_table.read().await);
133 tokio::task::yield_now().await;
134 }
135 }
136}
137
138fn admit(db: &ObjectDatabase, oid: &ObjectIdentifier) -> Result<(), Error> {
140 let object = db.get(oid).ok_or(Error::protocol(
141 ErrorClass::OBJECT,
142 ErrorCode::UNKNOWN_OBJECT,
143 ))?;
144 if oid.object_type() != ObjectType::ACCESS_POINT {
145 return Err(Error::protocol(
146 ErrorClass::OBJECT,
147 ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
148 ));
149 }
150 let healthy = match (
151 object.read_property(PropertyIdentifier::OUT_OF_SERVICE, None)?,
152 object.read_property(PropertyIdentifier::RELIABILITY, None)?,
153 ) {
154 (PropertyValue::Boolean(out_of_service), PropertyValue::Enumerated(reliability)) => {
155 !out_of_service && reliability == Reliability::NO_FAULT_DETECTED.to_raw()
156 }
157 _ => {
158 return Err(Error::protocol(
159 ErrorClass::PROPERTY,
160 ErrorCode::INVALID_DATA_TYPE,
161 ));
162 }
163 };
164 if !healthy {
165 return Err(Error::protocol(
166 ErrorClass::PROPERTY,
167 ErrorCode::WRITE_ACCESS_DENIED,
168 ));
169 }
170 if !object.supports_access_event_batch_internal() {
171 return Err(Error::protocol(
172 ErrorClass::OBJECT,
173 ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
174 ));
175 }
176 Ok(())
177}