use crate::audit_notification::{
AuditNotificationAuthorizationContext, UnconfirmedAuditNotificationAuthorizationContext,
};
use crate::device_communication_control::DeviceCommunicationControlAuthorizationContext;
use crate::life_safety::LifeSafetyOperationAuthorizationContext;
use crate::mutation::MutationAuthorizationContext;
use bacnet_encoding::npdu::NpduAddress;
pub use bacnet_transport::port::ScPrincipal;
use bacnet_transport::port::TransportProvenance;
#[cfg(feature = "sc-tls")]
pub use bacnet_transport::sc_tls::{ScCertificateMetadata, ScPeerCertificate, ScSubjectAltName};
use std::sync::Arc;
pub(crate) fn originating_principal(
provenance: &TransportProvenance,
source_network: Option<&NpduAddress>,
) -> Option<ScPrincipal> {
source_network
.is_none()
.then(|| provenance.principal())
.flatten()
}
#[cfg(feature = "sc-tls")]
pub(crate) fn originating_certificate<'a>(
provenance: &'a TransportProvenance,
source_network: Option<&NpduAddress>,
) -> Option<&'a ScPeerCertificate> {
source_network
.is_none()
.then(|| provenance.leaf_certificate())
.flatten()
}
macro_rules! certificate_accessors {
() => {
#[cfg(feature = "sc-tls")]
pub fn certificate(&self) -> Option<&$crate::policy::ScPeerCertificate> {
$crate::policy::originating_certificate(&self.provenance, self.source_network.as_ref())
}
#[cfg(feature = "sc-tls")]
pub fn forwarding_peer_certificate(&self) -> Option<&$crate::policy::ScPeerCertificate> {
self.provenance.leaf_certificate()
}
};
}
pub(crate) use certificate_accessors;
pub trait ServerPolicy: Send + Sync + 'static {
fn decides_mutations(&self) -> bool {
true
}
fn authorize_mutation(&self, context: &MutationAuthorizationContext) -> bool {
let _ = context;
true
}
fn authorize_life_safety_operation(
&self,
context: &LifeSafetyOperationAuthorizationContext,
) -> bool {
let _ = context;
false
}
fn authorize_device_communication_control(
&self,
context: &DeviceCommunicationControlAuthorizationContext,
) -> bool {
let _ = context;
true
}
fn authorize_audit_notification(
&self,
context: &AuditNotificationAuthorizationContext,
) -> bool {
let _ = context;
false
}
fn authorize_unconfirmed_audit_notification(
&self,
context: &UnconfirmedAuditNotificationAuthorizationContext,
) -> bool {
let _ = context;
false
}
}
type Decide<C> = Arc<dyn Fn(&C) -> bool + Send + Sync>;
#[derive(Clone, Default)]
pub struct FnPolicy {
mutation: Option<Decide<MutationAuthorizationContext>>,
life_safety_operation: Option<Decide<LifeSafetyOperationAuthorizationContext>>,
device_communication_control: Option<Decide<DeviceCommunicationControlAuthorizationContext>>,
audit_notification: Option<Decide<AuditNotificationAuthorizationContext>>,
unconfirmed_audit_notification:
Option<Decide<UnconfirmedAuditNotificationAuthorizationContext>>,
}
impl FnPolicy {
pub fn new() -> Self {
Self::default()
}
pub fn mutation(
mut self,
decide: impl Fn(&MutationAuthorizationContext) -> bool + Send + Sync + 'static,
) -> Self {
self.mutation = Some(Arc::new(decide));
self
}
pub fn life_safety_operation(
mut self,
decide: impl Fn(&LifeSafetyOperationAuthorizationContext) -> bool + Send + Sync + 'static,
) -> Self {
self.life_safety_operation = Some(Arc::new(decide));
self
}
pub fn device_communication_control(
mut self,
decide: impl Fn(&DeviceCommunicationControlAuthorizationContext) -> bool + Send + Sync + 'static,
) -> Self {
self.device_communication_control = Some(Arc::new(decide));
self
}
pub fn audit_notification(
mut self,
decide: impl Fn(&AuditNotificationAuthorizationContext) -> bool + Send + Sync + 'static,
) -> Self {
self.audit_notification = Some(Arc::new(decide));
self
}
pub fn unconfirmed_audit_notification(
mut self,
decide: impl Fn(&UnconfirmedAuditNotificationAuthorizationContext) -> bool
+ Send
+ Sync
+ 'static,
) -> Self {
self.unconfirmed_audit_notification = Some(Arc::new(decide));
self
}
}
impl<P: ServerPolicy + ?Sized> ServerPolicy for Arc<P> {
fn decides_mutations(&self) -> bool {
(**self).decides_mutations()
}
fn authorize_mutation(&self, context: &MutationAuthorizationContext) -> bool {
(**self).authorize_mutation(context)
}
fn authorize_life_safety_operation(
&self,
context: &LifeSafetyOperationAuthorizationContext,
) -> bool {
(**self).authorize_life_safety_operation(context)
}
fn authorize_device_communication_control(
&self,
context: &DeviceCommunicationControlAuthorizationContext,
) -> bool {
(**self).authorize_device_communication_control(context)
}
fn authorize_audit_notification(
&self,
context: &AuditNotificationAuthorizationContext,
) -> bool {
(**self).authorize_audit_notification(context)
}
fn authorize_unconfirmed_audit_notification(
&self,
context: &UnconfirmedAuditNotificationAuthorizationContext,
) -> bool {
(**self).authorize_unconfirmed_audit_notification(context)
}
}
impl<P: ServerPolicy + ?Sized> ServerPolicy for Box<P> {
fn decides_mutations(&self) -> bool {
(**self).decides_mutations()
}
fn authorize_mutation(&self, context: &MutationAuthorizationContext) -> bool {
(**self).authorize_mutation(context)
}
fn authorize_life_safety_operation(
&self,
context: &LifeSafetyOperationAuthorizationContext,
) -> bool {
(**self).authorize_life_safety_operation(context)
}
fn authorize_device_communication_control(
&self,
context: &DeviceCommunicationControlAuthorizationContext,
) -> bool {
(**self).authorize_device_communication_control(context)
}
fn authorize_audit_notification(
&self,
context: &AuditNotificationAuthorizationContext,
) -> bool {
(**self).authorize_audit_notification(context)
}
fn authorize_unconfirmed_audit_notification(
&self,
context: &UnconfirmedAuditNotificationAuthorizationContext,
) -> bool {
(**self).authorize_unconfirmed_audit_notification(context)
}
}
impl std::fmt::Debug for FnPolicy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FnPolicy")
.field("mutation", &self.mutation.is_some())
.field(
"life_safety_operation",
&self.life_safety_operation.is_some(),
)
.field(
"device_communication_control",
&self.device_communication_control.is_some(),
)
.field("audit_notification", &self.audit_notification.is_some())
.field(
"unconfirmed_audit_notification",
&self.unconfirmed_audit_notification.is_some(),
)
.finish()
}
}
impl ServerPolicy for FnPolicy {
fn decides_mutations(&self) -> bool {
self.mutation.is_some()
}
fn authorize_mutation(&self, context: &MutationAuthorizationContext) -> bool {
self.mutation.as_ref().is_none_or(|decide| decide(context))
}
fn authorize_life_safety_operation(
&self,
context: &LifeSafetyOperationAuthorizationContext,
) -> bool {
self.life_safety_operation
.as_ref()
.is_some_and(|decide| decide(context))
}
fn authorize_device_communication_control(
&self,
context: &DeviceCommunicationControlAuthorizationContext,
) -> bool {
self.device_communication_control
.as_ref()
.is_none_or(|decide| decide(context))
}
fn authorize_audit_notification(
&self,
context: &AuditNotificationAuthorizationContext,
) -> bool {
self.audit_notification
.as_ref()
.is_some_and(|decide| decide(context))
}
fn authorize_unconfirmed_audit_notification(
&self,
context: &UnconfirmedAuditNotificationAuthorizationContext,
) -> bool {
self.unconfirmed_audit_notification
.as_ref()
.is_some_and(|decide| decide(context))
}
}
#[cfg(test)]
#[path = "policy_tests.rs"]
mod tests;