pub fn evaluate_accompaniment(
db: &ObjectDatabase,
request: AccompanimentRequest<'_>,
) -> Result<AccompanimentEvaluation, Error>Expand description
Evaluate local accompaniment evidence without writing objects, obtaining a clock, performing I/O, or publishing an event. Rights are recomputed in this database borrow; a caller-provided grant is never accepted as authority.
This is only an accompaniment stage. Authentication, credential activity,
other authorization checks, pending transactions, and exactly-once event
publication belong to the caller. See AccompanimentRequest for explicit
timing and bounded-chain evidence; no default order or timeout is installed.
A direct attempt using the original credential is incorrect. A chain that returns to an earlier credential after a distinct companion is unresolved; no cyclic authorization fixed point is inferred. Missing witness rows also remain unresolved, while an explicit Waiting row can reach a verified expiry. Intents refer only to the requested original transaction, including when a child’s failure invalidates its companion.
§Example
use bacnet_objects::{access_control::*, database::ObjectDatabase};
use bacnet_types::{
constructed::{BACnetAccessRule, BACnetAssignedAccessRights},
enums::ObjectType,
primitives::ObjectIdentifier,
};
use core::time::Duration;
let id = |kind, n| ObjectIdentifier::new(kind, n).unwrap();
let point = id(ObjectType::ACCESS_POINT, 1);
let mut db = ObjectDatabase::new();
db.add(Box::new(AccessPointObject::new(1, "entry")?))?;
for n in 1..=2 {
let rights_id = id(ObjectType::ACCESS_RIGHTS, n);
let mut rights = AccessRightsObject::new(n, format!("rights-{n}"))?;
rights.set_positive_access_rules([
BACnetAccessRule::new(None, Some(point.into()), true),
])?;
if n == 1 {
rights.set_accompaniment(Some(id(ObjectType::ACCESS_CREDENTIAL, 2).into()))?;
}
let mut card = AccessCredentialObject::new(n, format!("card-{n}"))?;
card.set_assigned_access_rights(vec![BACnetAssignedAccessRights {
assigned_access_rights: rights_id.into(), enable: true,
}])?;
db.add(Box::new(rights))?;
db.add(Box::new(card))?;
}
let original = CredentialPresentation {
sequence: 1, credential: id(ObjectType::ACCESS_CREDENTIAL, 1),
point, at: Duration::from_secs(20),
};
let companion = CredentialPresentation {
sequence: 2, credential: id(ObjectType::ACCESS_CREDENTIAL, 2),
point, at: Duration::from_secs(21),
};
let witnesses = [AccompanimentWitness {
original: original.sequence,
evidence: AccompanimentEvidence::Presented(companion),
}];
let result = evaluate_accompaniment(&db, AccompanimentRequest {
original, now: companion.at, witnesses: &witnesses,
timing: AccompanimentTiming {
order: AccompanimentOrder::After,
fallback_period: Duration::from_secs(5),
deadline: AccompanimentDeadline::Inclusive,
},
max_links: 8,
})?;
assert_eq!(result.outcome, AccompanimentOutcome::Satisfied);
assert_eq!(result.events.len(), 1);
// The application still checks authentication and all other authorization
// criteria before acting; it owns transaction state and event publication.§Errors
Original credential/point or original rights-read errors are returned from
evaluate_access_rights. Unavailable accompaniment/companion evidence is an
AccompanimentOutcome::Unresolved result instead.