bacnet_objects/access_control/
credential.rs1use std::sync::Arc;
2
3use bacnet_encoding::constructed::{
4 encode_assigned_access_rights, encode_credential_authentication_factor,
5};
6use bacnet_types::constructed::{BACnetAssignedAccessRights, BACnetCredentialAuthenticationFactor};
7use bacnet_types::enums::{AccessCredentialDisable, AccessCredentialDisableReason};
8use bytes::BytesMut;
9
10use super::credential_rules::{self as rules, WindowLimit};
11use super::*;
12use crate::clock::ClockReader;
13
14pub struct AccessCredentialObject {
40 oid: ObjectIdentifier,
41 name: String,
42 description: String,
43 global_identifier: u32,
44 local_reasons: Vec<AccessCredentialDisableReason>,
46 credential_disable: AccessCredentialDisable,
47 activation_time: WindowLimit,
48 expiration_time: WindowLimit,
49 authentication_factors: Vec<BACnetCredentialAuthenticationFactor>,
50 assigned_access_rights: Vec<BACnetAssignedAccessRights>,
51 status_flags: StatusFlags,
52 reliability: Reliability,
53 clock: Option<Arc<dyn ClockReader>>,
55}
56
57impl AccessCredentialObject {
58 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
62 let oid = ObjectIdentifier::new(ObjectType::ACCESS_CREDENTIAL, instance)?;
63 Ok(Self {
64 oid,
65 name: name.into(),
66 description: String::new(),
67 global_identifier: 0,
68 local_reasons: Vec::new(),
69 credential_disable: AccessCredentialDisable::NONE,
70 activation_time: WindowLimit::OPEN,
71 expiration_time: WindowLimit::OPEN,
72 authentication_factors: Vec::new(),
73 assigned_access_rights: Vec::new(),
74 status_flags: StatusFlags::empty(),
75 reliability: Reliability::NO_FAULT_DETECTED,
76 clock: None,
77 })
78 }
79
80 pub fn set_global_identifier(&mut self, value: u32) {
82 self.global_identifier = value;
83 }
84
85 pub fn set_credential_disable(&mut self, value: AccessCredentialDisable) -> Result<(), Error> {
88 rules::check_credential_disable(value)?;
89 self.credential_disable = value;
90 Ok(())
91 }
92
93 pub fn set_activation_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
96 self.activation_time = WindowLimit::new(date, time)?;
97 Ok(())
98 }
99
100 pub fn set_expiration_time(&mut self, date: Date, time: Time) -> Result<(), Error> {
103 self.expiration_time = WindowLimit::new(date, time)?;
104 Ok(())
105 }
106
107 pub fn activation_time(&self) -> (Date, Time) {
109 self.activation_time.parts()
110 }
111
112 pub fn expiration_time(&self) -> (Date, Time) {
114 self.expiration_time.parts()
115 }
116
117 pub fn add_disable_reason(
125 &mut self,
126 reason: AccessCredentialDisableReason,
127 ) -> Result<(), Error> {
128 rules::check_local_reason(reason)?;
129 if let Err(index) = self
130 .local_reasons
131 .binary_search_by_key(&reason.to_raw(), |r| r.to_raw())
132 {
133 self.local_reasons.insert(index, reason);
134 }
135 Ok(())
136 }
137
138 pub fn remove_disable_reason(&mut self, reason: AccessCredentialDisableReason) -> bool {
141 let before = self.local_reasons.len();
142 self.local_reasons.retain(|r| *r != reason);
143 self.local_reasons.len() != before
144 }
145
146 pub fn reason_for_disable(&self) -> Vec<AccessCredentialDisableReason> {
149 let frame = self.clock.as_ref().and_then(|clock| clock.read_clock());
150 let mut reasons = self.local_reasons.clone();
151 reasons.extend(rules::credential_disable_reason(self.credential_disable));
152 reasons.extend(rules::window_reasons(
153 frame,
154 self.activation_time,
155 self.expiration_time,
156 ));
157 reasons.sort_by_key(|r| r.to_raw());
158 reasons.dedup();
159 reasons
160 }
161
162 pub fn credential_status(&self) -> BinaryPV {
164 if self.reason_for_disable().is_empty() {
165 BinaryPV::ACTIVE
166 } else {
167 BinaryPV::INACTIVE
168 }
169 }
170
171 pub fn set_authentication_factors(
175 &mut self,
176 factors: Vec<BACnetCredentialAuthenticationFactor>,
177 ) -> Result<(), Error> {
178 factors
179 .iter()
180 .try_for_each(rules::check_authentication_factor)?;
181 self.authentication_factors = factors;
182 Ok(())
183 }
184
185 pub fn authentication_factors(&self) -> &[BACnetCredentialAuthenticationFactor] {
187 &self.authentication_factors
188 }
189
190 pub fn set_assigned_access_rights(
195 &mut self,
196 rights: Vec<BACnetAssignedAccessRights>,
197 ) -> Result<(), Error> {
198 rights
199 .iter()
200 .try_for_each(rules::check_assigned_access_rights)?;
201 self.assigned_access_rights = rights;
202 Ok(())
203 }
204
205 pub fn assigned_access_rights(&self) -> &[BACnetAssignedAccessRights] {
207 &self.assigned_access_rights
208 }
209}
210
211fn framed<T>(items: &[T], encode: fn(&mut BytesMut, &T)) -> Vec<PropertyValue> {
213 items
214 .iter()
215 .map(|item| {
216 let mut buf = BytesMut::new();
217 encode(&mut buf, item);
218 PropertyValue::ApplicationData(buf.to_vec())
219 })
220 .collect()
221}
222
223impl BACnetObject for AccessCredentialObject {
224 fn object_identifier(&self) -> ObjectIdentifier {
225 self.oid
226 }
227
228 fn object_name(&self) -> &str {
229 &self.name
230 }
231
232 fn read_property(
233 &self,
234 property: PropertyIdentifier,
235 array_index: Option<u32>,
236 ) -> Result<PropertyValue, Error> {
237 if let Some(result) =
240 read_common_properties!(self, property, array_index, no_out_of_service)
241 {
242 return result;
243 }
244 match property {
245 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
246 ObjectType::ACCESS_CREDENTIAL.to_raw(),
247 )),
248 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
249 Ok(PropertyValue::Unsigned(self.global_identifier.into()))
250 }
251 p if p == PropertyIdentifier::CREDENTIAL_STATUS => {
252 Ok(PropertyValue::Enumerated(self.credential_status().to_raw()))
253 }
254 p if p == PropertyIdentifier::REASON_FOR_DISABLE => Ok(PropertyValue::List(
255 self.reason_for_disable()
256 .into_iter()
257 .map(|reason| PropertyValue::Enumerated(reason.to_raw()))
258 .collect(),
259 )),
260 p if p == PropertyIdentifier::AUTHENTICATION_FACTORS => common::read_array(
261 framed(
262 &self.authentication_factors,
263 encode_credential_authentication_factor,
264 ),
265 array_index,
266 ),
267 p if p == PropertyIdentifier::ACTIVATION_TIME => Ok(self.activation_time.to_property()),
268 p if p == PropertyIdentifier::EXPIRATION_TIME => Ok(self.expiration_time.to_property()),
269 p if p == PropertyIdentifier::CREDENTIAL_DISABLE => {
270 Ok(PropertyValue::Enumerated(self.credential_disable.to_raw()))
271 }
272 p if p == PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS => common::read_array(
273 framed(&self.assigned_access_rights, encode_assigned_access_rights),
274 array_index,
275 ),
276 _ => Err(common::unknown_property_error()),
277 }
278 }
279
280 fn write_property(
281 &mut self,
282 property: PropertyIdentifier,
283 array_index: Option<u32>,
284 value: PropertyValue,
285 _priority: Option<u8>,
286 ) -> Result<(), Error> {
287 if let Some(result) = common::write_description(&mut self.description, property, &value) {
288 return result;
289 }
290 match property {
291 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
292 let PropertyValue::Unsigned(raw) = value else {
293 return Err(common::invalid_data_type_error());
294 };
295 self.global_identifier = common::u64_to_u32(raw)?;
296 Ok(())
297 }
298 p if p == PropertyIdentifier::CREDENTIAL_DISABLE => {
299 let PropertyValue::Enumerated(raw) = value else {
300 return Err(common::invalid_data_type_error());
301 };
302 self.set_credential_disable(AccessCredentialDisable::from_raw(raw))
303 }
304 p if p == PropertyIdentifier::ACTIVATION_TIME => {
305 self.activation_time = WindowLimit::from_property(value)?;
306 Ok(())
307 }
308 p if p == PropertyIdentifier::EXPIRATION_TIME => {
309 self.expiration_time = WindowLimit::from_property(value)?;
310 Ok(())
311 }
312 _ => Err(crate::common::unhandled_write_error(
315 self.property_metadata().as_ref(),
316 property,
317 array_index,
318 )),
319 }
320 }
321
322 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
323 super::metadata_identity::for_access_credential_object(self)
324 }
325
326 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
327 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
328 }
329
330 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
331 self.clock = clock;
332 }
333}
334
335