Skip to main content

bacnet_objects/access_control/
mod.rs

1//! Access Control objects (ASHRAE 135-2020 Clause 12).
2//!
3//! This module implements the seven BACnet access control object types:
4//! - AccessDoor (type 30)
5//! - AccessCredential (type 32)
6//! - AccessPoint (type 33)
7//! - AccessRights (type 34)
8//! - AccessUser (type 35)
9//! - AccessZone (type 36)
10//! - CredentialDataInput (type 37)
11//!
12//! [`evaluate_access_rights`] checks a credential against the Access Rights
13//! it is assigned, for the application to act on (#1331). Separate
14//! [`evaluate_accompaniment`] combines those rights with explicit local companion
15//! evidence and returns accompaniment-stage results and event intents (#1571).
16//! Neither stage performs complete authorization or publishes events.
17//! [`evaluate_authorization`] composes the stages with explicit application
18//! evidence and policy for a pure complete local authorization pass (#1572).
19
20use crate::prelude::*;
21use alloc::borrow::Cow;
22use alloc::sync::Arc;
23use bacnet_encoding::primitives::encode_timestamp_choice;
24use bacnet_types::constructed::BACnetDeviceObjectReference;
25use bacnet_types::enums::{
26    AccessEvent, AccessUserType, AuthenticationStatus, BinaryPV, DoorAlarmState, DoorSecuredStatus,
27    DoorStatus, DoorValue, EventState, LockStatus, ObjectType, PropertyIdentifier, Reliability,
28};
29use bacnet_types::error::Error;
30use bacnet_types::primitives::{
31    BACnetTimeStamp, Date, ObjectIdentifier, PropertyValue, StatusFlags, Time,
32};
33use bytes::BytesMut;
34
35use crate::clock::{ClockReader, current_datetime};
36use crate::common::{self, read_common_properties};
37use crate::traits::BACnetObject;
38
39/// The time stamp an Access Point's Access_Event_Time and a Credential Data
40/// Input's Update_Time hold before their first update: the date-and-time form
41/// with every octet unspecified (Clauses 12.31.29 and 12.36.11).
42fn never_updated() -> BACnetTimeStamp {
43    BACnetTimeStamp::DateTime {
44        date: Date {
45            year: Date::UNSPECIFIED,
46            month: Date::UNSPECIFIED,
47            day: Date::UNSPECIFIED,
48            day_of_week: Date::UNSPECIFIED,
49        },
50        time: Time {
51            hour: Time::UNSPECIFIED,
52            minute: Time::UNSPECIFIED,
53            second: Time::UNSPECIFIED,
54            hundredths: Time::UNSPECIFIED,
55        },
56    }
57}
58
59/// A `BACnetTimeStamp` property in its Clause 21 CHOICE form.
60fn timestamp_value(stamp: &BACnetTimeStamp) -> Result<PropertyValue, Error> {
61    let mut buf = BytesMut::new();
62    encode_timestamp_choice(&mut buf, stamp)?;
63    Ok(PropertyValue::ApplicationData(buf.to_vec()))
64}
65
66/// The BACnetTimeStamp (Clause 21.6) for an update recorded now: the Device
67/// clock's date and time, or `sequence()` in the sequence-number form when
68/// there is no usable clock.
69///
70/// Without a clock every date-and-time stamp would be the unspecified one,
71/// so successive updates would carry the same time and a subscriber watching
72/// that time (Table 13-1) would hear none of them. A sequence number moves on
73/// with each update instead; Clauses 12.31.29 and 12.36.11 both allow an
74/// update time in that form.
75fn update_stamp(
76    clock: Option<&dyn ClockReader>,
77    sequence: impl FnOnce() -> u16,
78) -> BACnetTimeStamp {
79    match current_datetime(clock) {
80        Some((date, time)) => BACnetTimeStamp::DateTime { date, time },
81        None => BACnetTimeStamp::SequenceNumber(sequence()),
82    }
83}
84
85/// The sequence number after `previous`: it climbs from 1 to 65535, the top
86/// of the production's range, and starts again at 1. It never takes 0,
87/// which marks an update time with no update yet (Clauses 12.31.29 and
88/// 12.36.11).
89fn next_sequence(previous: u16) -> u16 {
90    if previous == u16::MAX {
91        1
92    } else {
93        previous + 1
94    }
95}
96
97/// The Device clock an object stamps its update times from, and the
98/// sequence numbers it hands out, in order, while that clock gives no usable
99/// reading (`update_stamp`).
100#[derive(Default)]
101struct UpdateClock {
102    clock: Option<Arc<dyn ClockReader>>,
103    /// The last sequence number handed out; 0 before the first.
104    sequence: u16,
105}
106
107impl UpdateClock {
108    /// Stamp an update made now.
109    fn stamp(&mut self) -> BACnetTimeStamp {
110        let sequence = &mut self.sequence;
111        update_stamp(self.clock.as_deref(), || {
112            *sequence = next_sequence(*sequence);
113            *sequence
114        })
115    }
116}
117
118/// A client's simulated Reliability, written while Out_Of_Service is TRUE:
119/// an Enumerated inside the BACnetReliability production. Another datatype is
120/// INVALID_DATA_TYPE and another number VALUE_OUT_OF_RANGE.
121fn simulated_reliability(value: &PropertyValue) -> Result<Reliability, Error> {
122    let PropertyValue::Enumerated(raw) = value else {
123        return Err(common::invalid_data_type_error());
124    };
125    checked_reliability(Reliability::from_raw(*raw))
126}
127
128/// `reliability`, or VALUE_OUT_OF_RANGE outside the BACnetReliability
129/// production.
130fn checked_reliability(reliability: Reliability) -> Result<Reliability, Error> {
131    if common::is_reliability_value_valid(reliability) {
132        Ok(reliability)
133    } else {
134        Err(common::value_out_of_range_error())
135    }
136}
137
138/// The references a setter of one of the reference lists or arrays stores,
139/// each naming an object of `object_type` in this device or in the Device its
140/// device identifier names. A reference to another object type, or one whose
141/// device identifier isn't a Device (the shared `check_device_member`), is
142/// VALUE_OUT_OF_RANGE, so the setter keeps what it held.
143fn references_to(
144    object_type: ObjectType,
145    references: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
146) -> Result<Vec<BACnetDeviceObjectReference>, Error> {
147    let references: Vec<BACnetDeviceObjectReference> =
148        references.into_iter().map(Into::into).collect();
149    for reference in &references {
150        crate::device_reference::check_device_member(reference.device_identifier)?;
151        if reference.object_identifier.object_type() != object_type {
152            return Err(common::value_out_of_range_error());
153        }
154    }
155    Ok(references)
156}
157
158// ---------------------------------------------------------------------------
159
160mod accompaniment;
161mod accompaniment_types;
162mod authorization;
163mod authorization_context;
164mod authorization_types;
165mod credential;
166mod credential_data_input;
167mod credential_data_input_formats;
168mod credential_data_input_out_of_service;
169mod credential_rules;
170mod door;
171mod door_alarm;
172mod door_out_of_service;
173mod input;
174mod metadata_identity;
175mod metadata_topology;
176mod point;
177mod point_authorization;
178mod point_event_time;
179mod rights;
180mod rights_evaluation;
181mod rights_writes;
182mod user;
183mod zone;
184mod zone_occupancy;
185mod zone_out_of_service;
186pub use accompaniment::*;
187pub use accompaniment_types::*;
188pub use authorization::*;
189pub use authorization_context::*;
190pub use authorization_types::*;
191pub use credential::*;
192pub use credential_data_input::*;
193pub use door::*;
194pub use input::*;
195pub use point::*;
196pub use rights::*;
197pub use rights_evaluation::*;
198pub use user::*;
199pub use zone::*;
200
201#[cfg(test)]
202mod array_tests;
203#[cfg(test)]
204mod constructed_value_tests;
205#[cfg(test)]
206mod credential_data_input_format_tests;
207#[cfg(test)]
208mod credential_data_input_out_of_service_tests;
209#[cfg(test)]
210mod credential_exemption_tests;
211#[cfg(test)]
212mod credential_tests;
213#[cfg(test)]
214mod device_reference_tests;
215#[cfg(test)]
216mod door_alarm_tests;
217#[cfg(test)]
218mod door_out_of_service_tests;
219#[cfg(test)]
220mod door_pulse_tests;
221#[cfg(test)]
222mod input_tests;
223#[cfg(test)]
224mod point_authorization_tests;
225#[cfg(test)]
226mod point_out_of_service_tests;
227#[cfg(test)]
228mod point_policy_tests;
229#[cfg(test)]
230mod point_status_tests;
231#[cfg(test)]
232mod tests;
233#[cfg(test)]
234mod typed_value_tests;
235#[cfg(test)]
236mod user_references_tests;
237#[cfg(test)]
238mod zone_event_tests;
239#[cfg(test)]
240mod zone_occupancy_tests;
241#[cfg(test)]
242mod zone_out_of_service_tests;
243#[cfg(test)]
244mod zone_points_tests;
245
246#[cfg(test)]
247mod accompaniment_tests;
248
249#[cfg(test)]
250mod accompaniment_test_support;
251
252#[cfg(test)]
253mod accompaniment_chain_tests;
254#[cfg(test)]
255mod accompaniment_resolution_tests;
256#[cfg(test)]
257mod accompaniment_timing_tests;
258
259#[cfg(test)]
260mod authorization_credential_tests;
261#[cfg(test)]
262mod authorization_test_support;
263
264#[cfg(test)]
265mod authorization_checks_tests;
266#[cfg(test)]
267mod authorization_occupancy_tests;
268#[cfg(test)]
269mod authorization_passback_tests;
270
271#[cfg(test)]
272mod authorization_phase_tests;
273
274#[cfg(test)]
275mod authorization_companion_tests;
276#[cfg(test)]
277mod authorization_rights_tests;
278
279#[cfg(test)]
280mod authorization_passback_reporting_tests;