Skip to main content

bacnet_server/
audit_notification.rs

1//! Receiver policy for confirmed and unconfirmed AuditNotification services.
2
3use bacnet_encoding::npdu::NpduAddress;
4use bacnet_services::audit::AuditNotificationRequest;
5use bacnet_transport::port::TransportProvenance;
6use bacnet_types::MacAddr;
7use bacnet_types::primitives::ObjectIdentifier;
8
9/// Local maximum accepted AuditNotification service payload.
10pub const MAX_AUDIT_NOTIFICATION_BYTES: usize = 64 * 1024;
11/// Local maximum number of notifications in one accepted request.
12pub const MAX_AUDIT_NOTIFICATIONS: usize = 256;
13
14/// Transport provenance and decoded content supplied to
15/// [`ServerPolicy::authorize_audit_notification`](crate::ServerPolicy::authorize_audit_notification).
16///
17/// The source/target identities inside `request` are peer-reported audit data,
18/// not authenticated transport provenance. Policy should use `source_network`
19/// for a usable routed origin and `source_mac` for the immediate data-link peer.
20/// `Debug` is redacted: address lengths, the sink, whether a principal is
21/// present and the notification count, never the notifications themselves.
22#[derive(Clone, PartialEq)]
23pub struct AuditNotificationAuthorizationContext {
24    /// Immediate data-link peer (normally a router for routed traffic).
25    pub source_mac: MacAddr,
26    /// Originating NPDU source when one was present.
27    pub source_network: Option<NpduAddress>,
28    /// Honest transport + origin provenance for this ingress; the source of
29    /// the principal, if any.
30    pub provenance: TransportProvenance,
31    /// Outer Confirmed-Request invoke identifier.
32    pub invoke_id: u8,
33    /// Explicitly configured local Audit Log sink.
34    pub audit_log_sink: ObjectIdentifier,
35    /// Decoded peer-reported notification list, preserved verbatim.
36    pub request: AuditNotificationRequest,
37}
38
39impl AuditNotificationAuthorizationContext {
40    /// The principal of the peer that originated the request: the verified
41    /// leaf certificate of a direct BACnet/SC connection, when the request
42    /// carries no routed source. `None` for other ingress, hub-relayed and
43    /// routed requests included (see [`crate::policy`]).
44    pub fn principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
45        crate::policy::originating_principal(&self.provenance, self.source_network.as_ref())
46    }
47
48    /// The principal of the direct BACnet/SC peer that handed this request
49    /// over, a router included when the request was routed; `None` off a
50    /// verified direct SC connection. Trusting it trusts everything that
51    /// peer forwards.
52    pub fn forwarding_peer_principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
53        self.provenance.principal()
54    }
55
56    crate::policy::certificate_accessors!();
57}
58
59/// Transport provenance and decoded content supplied to
60/// [`ServerPolicy::authorize_unconfirmed_audit_notification`](crate::ServerPolicy::authorize_unconfirmed_audit_notification).
61///
62/// The source/target identities inside `request` are peer-reported audit data,
63/// not authenticated transport provenance. Policy should use `source_network`
64/// for a usable routed origin and `source_mac` for the immediate data-link peer.
65/// `Debug` is redacted: address lengths, the sink, whether a principal is
66/// present and the notification count, never the notifications themselves.
67#[derive(Clone, PartialEq)]
68pub struct UnconfirmedAuditNotificationAuthorizationContext {
69    /// Immediate data-link peer (normally a router for routed traffic).
70    pub source_mac: MacAddr,
71    /// Originating NPDU source when one was present.
72    pub source_network: Option<NpduAddress>,
73    /// Honest transport + origin provenance for this ingress; the source of
74    /// the principal, if any.
75    pub provenance: TransportProvenance,
76    /// Explicitly configured local Audit Log sink.
77    pub audit_log_sink: ObjectIdentifier,
78    /// Decoded peer-reported notification list, preserved verbatim.
79    pub request: AuditNotificationRequest,
80}
81
82impl UnconfirmedAuditNotificationAuthorizationContext {
83    /// The principal of the peer that originated the request: the verified
84    /// leaf certificate of a direct BACnet/SC connection, when the request
85    /// carries no routed source. `None` for other ingress, hub-relayed and
86    /// routed requests included (see [`crate::policy`]).
87    pub fn principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
88        crate::policy::originating_principal(&self.provenance, self.source_network.as_ref())
89    }
90
91    /// The principal of the direct BACnet/SC peer that handed this request
92    /// over, a router included when the request was routed; `None` off a
93    /// verified direct SC connection. Trusting it trusts everything that
94    /// peer forwards.
95    pub fn forwarding_peer_principal(&self) -> Option<bacnet_transport::port::ScPrincipal> {
96        self.provenance.principal()
97    }
98
99    crate::policy::certificate_accessors!();
100}
101
102impl std::fmt::Debug for AuditNotificationAuthorizationContext {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        f.debug_struct("AuditNotificationAuthorizationContext")
105            .field("source_mac_len", &self.source_mac.len())
106            .field(
107                "source_network",
108                &self
109                    .source_network
110                    .as_ref()
111                    .map(|source| (source.network, source.mac_address.len())),
112            )
113            .field("provenance", &self.provenance)
114            .field("principal", &self.principal().is_some())
115            .field("invoke_id", &self.invoke_id)
116            .field("audit_log_sink", &self.audit_log_sink)
117            .field("notifications", &self.request.notifications.len())
118            .finish_non_exhaustive()
119    }
120}
121
122impl std::fmt::Debug for UnconfirmedAuditNotificationAuthorizationContext {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.debug_struct("UnconfirmedAuditNotificationAuthorizationContext")
125            .field("source_mac_len", &self.source_mac.len())
126            .field(
127                "source_network",
128                &self
129                    .source_network
130                    .as_ref()
131                    .map(|source| (source.network, source.mac_address.len())),
132            )
133            .field("provenance", &self.provenance)
134            .field("principal", &self.principal().is_some())
135            .field("audit_log_sink", &self.audit_log_sink)
136            .field("notifications", &self.request.notifications.len())
137            .finish_non_exhaustive()
138    }
139}