Skip to main content

bacnet_objects/access_control/
user.rs

1use super::*;
2
3// AccessUserObject (type 35)
4// ---------------------------------------------------------------------------
5
6/// BACnet Access User object (type 35).
7///
8/// Stands for whoever or whatever is granted access (someone, a team, a
9/// tracked item) and the credentials it holds. Its kind lives in User_Type;
10/// Table 12-38 has no Present_Value, Assigned_Access_Rights or Out_Of_Service
11/// row, so the object serves none of them (#1064).
12///
13/// Credentials, Members and Member_Of are lists of
14/// `BACnetDeviceObjectReference` (Clauses 12.33.12 to 12.33.14; #1394), so an
15/// entry may name an object in another device. Credentials names the user's
16/// Access Credential objects. Members and Member_Of name other Access Users,
17/// one level down and one level up a hierarchy of users (a department and
18/// the people in it, say). The application sets all three; they are
19/// read-only over the network.
20///
21/// Global_Identifier names the user across devices: every Access User
22/// standing for the same person, team or item carries the same nonzero
23/// value, and 0 means none is assigned (Clause 12.33.5). It is the W row of
24/// Table 12-38, so clients write it as they write an Access Credential's
25/// (#1463).
26pub struct AccessUserObject {
27    oid: ObjectIdentifier,
28    name: String,
29    description: String,
30    global_identifier: u32,
31    user_type: AccessUserType,
32    credentials: Vec<BACnetDeviceObjectReference>,
33    members: Vec<BACnetDeviceObjectReference>,
34    member_of: Vec<BACnetDeviceObjectReference>,
35    status_flags: StatusFlags,
36    reliability: Reliability,
37}
38
39impl AccessUserObject {
40    /// Create a new Access User object.
41    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
42        let oid = ObjectIdentifier::new(ObjectType::ACCESS_USER, instance)?;
43        Ok(Self {
44            oid,
45            name: name.into(),
46            description: String::new(),
47            global_identifier: 0,
48            user_type: AccessUserType::ASSET,
49            credentials: Vec::new(),
50            members: Vec::new(),
51            member_of: Vec::new(),
52            status_flags: StatusFlags::empty(),
53            reliability: Reliability::NO_FAULT_DETECTED,
54        })
55    }
56
57    /// Set Global_Identifier; 0 means none is assigned (Clause 12.33.5).
58    pub fn set_global_identifier(&mut self, value: u32) {
59        self.global_identifier = value;
60    }
61
62    /// Set Credentials, the Access Credential objects the user holds
63    /// (Clause 12.33.14). A reference with no device identifier names an
64    /// object in this device.
65    ///
66    /// Each reference has to name an Access Credential object, and its
67    /// device identifier, when given, a Device object; a list breaking either
68    /// rule is refused with VALUE_OUT_OF_RANGE and the credentials set before
69    /// are kept. The list is read-only over the network.
70    pub fn set_credentials(
71        &mut self,
72        credentials: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
73    ) -> Result<(), Error> {
74        self.credentials = references_to(ObjectType::ACCESS_CREDENTIAL, credentials)?;
75        Ok(())
76    }
77
78    /// Set Members, the Access Users one level below this one
79    /// (Clause 12.33.12), with the checks [`Self::set_credentials`] makes,
80    /// for Access User objects.
81    pub fn set_members(
82        &mut self,
83        members: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
84    ) -> Result<(), Error> {
85        self.members = references_to(ObjectType::ACCESS_USER, members)?;
86        Ok(())
87    }
88
89    /// Set Member_Of, the Access Users one level above this one
90    /// (Clause 12.33.13), with the checks [`Self::set_members`] makes.
91    pub fn set_member_of(
92        &mut self,
93        groups: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
94    ) -> Result<(), Error> {
95        self.member_of = references_to(ObjectType::ACCESS_USER, groups)?;
96        Ok(())
97    }
98}
99
100impl BACnetObject for AccessUserObject {
101    fn object_identifier(&self) -> ObjectIdentifier {
102        self.oid
103    }
104
105    fn object_name(&self) -> &str {
106        &self.name
107    }
108
109    fn read_property(
110        &self,
111        property: PropertyIdentifier,
112        array_index: Option<u32>,
113    ) -> Result<PropertyValue, Error> {
114        // Clause 12.33 holds the OUT_OF_SERVICE flag FALSE.
115        if let Some(result) =
116            read_common_properties!(self, property, array_index, no_out_of_service)
117        {
118            return result;
119        }
120        match property {
121            p if p == PropertyIdentifier::OBJECT_TYPE => {
122                Ok(PropertyValue::Enumerated(ObjectType::ACCESS_USER.to_raw()))
123            }
124            p if p == PropertyIdentifier::USER_TYPE => {
125                Ok(PropertyValue::Enumerated(self.user_type.to_raw()))
126            }
127            p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
128                Ok(PropertyValue::Unsigned(self.global_identifier.into()))
129            }
130            p if p == PropertyIdentifier::CREDENTIALS => {
131                Ok(crate::device_reference::reference_list(&self.credentials))
132            }
133            p if p == PropertyIdentifier::MEMBERS => {
134                Ok(crate::device_reference::reference_list(&self.members))
135            }
136            p if p == PropertyIdentifier::MEMBER_OF => {
137                Ok(crate::device_reference::reference_list(&self.member_of))
138            }
139            _ => Err(common::unknown_property_error()),
140        }
141    }
142
143    fn write_property(
144        &mut self,
145        property: PropertyIdentifier,
146        _array_index: Option<u32>,
147        value: PropertyValue,
148        _priority: Option<u8>,
149    ) -> Result<(), Error> {
150        if let Some(result) = common::write_description(&mut self.description, property, &value) {
151            return result;
152        }
153        match property {
154            p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
155                let PropertyValue::Unsigned(raw) = value else {
156                    return Err(common::invalid_data_type_error());
157                };
158                self.global_identifier = common::u64_to_u32(raw)?;
159                Ok(())
160            }
161            p if p == PropertyIdentifier::USER_TYPE => {
162                if let PropertyValue::Enumerated(v) = value {
163                    self.user_type = AccessUserType::from_raw(v);
164                    Ok(())
165                } else {
166                    Err(common::invalid_data_type_error())
167                }
168            }
169            _ => Err(crate::common::unhandled_write_error(
170                self.property_metadata().as_ref(),
171                property,
172                _array_index,
173            )),
174        }
175    }
176
177    fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
178        super::metadata_identity::for_access_user_object(self)
179    }
180
181    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
182        crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
183    }
184}
185
186// ---------------------------------------------------------------------------