Skip to main content

krabka_audit/
ocsf.rs

1//! OCSF serialization for audit events.
2//!
3//! OCSF is the Open Cybersecurity Schema Framework.
4//!
5//! [`to_ocsf`] is the one entry point. It matches an [`AuditEvent`] variant and
6//! hands it to the child module that builds the OCSF record class for that
7//! variant, so each class's field names and id numbers live in exactly one
8//! place. [`ProductInfo`] names the emitting product in every record.
9
10use {
11    self::{
12        api_activity::{PrivilegedFields, ocsf_admin_operation, ocsf_privileged_action},
13        authentication::ocsf_authentication,
14        authorization::ocsf_authorization_denied,
15        lifecycle::ocsf_lifecycle,
16    },
17    crate::{event::AuditEvent, ids::EpochMs},
18    krabka_ids::NodeId,
19};
20
21mod api_activity;
22mod authentication;
23mod authorization;
24mod envelope;
25mod lifecycle;
26
27#[cfg(test)]
28mod tests;
29
30/// Product identity for every OCSF record's `metadata` field.
31#[derive(Debug, Clone)]
32pub struct ProductInfo {
33    pub vendor_name: String,
34    pub name: String,
35    pub version: String,
36}
37
38/// Serialize an [`AuditEvent`] to an OCSF JSON object.
39#[must_use]
40pub fn to_ocsf(event: &AuditEvent, product: &ProductInfo) -> serde_json::Value {
41    match event {
42        AuditEvent::Authentication {
43            outcome,
44            mechanism,
45            principal,
46            source,
47            reason,
48            time_ms,
49        } => ocsf_authentication(
50            *outcome,
51            mechanism,
52            principal,
53            source,
54            reason.as_ref(),
55            *time_ms,
56            product,
57        ),
58        AuditEvent::AuthorizationDenied {
59            principal,
60            source,
61            resource_type,
62            resource_name,
63            operation,
64            time_ms,
65        } => ocsf_authorization_denied(
66            principal,
67            source,
68            resource_type,
69            resource_name,
70            operation,
71            *time_ms,
72            product,
73        ),
74        AuditEvent::AdminOperation {
75            outcome,
76            principal,
77            source,
78            operation,
79            resources,
80            time_ms,
81        } => ocsf_admin_operation(
82            *outcome, principal, source, operation, resources, *time_ms, product,
83        ),
84        AuditEvent::PrivilegedAction {
85            outcome,
86            phase,
87            action,
88            target,
89            proposal_id,
90            principal,
91            counterparties,
92            approver_set_fingerprint,
93            key_id,
94            signature,
95            signature_verified,
96            signed_at_ms,
97            source,
98            reason,
99            time_ms,
100        } => ocsf_privileged_action(
101            &PrivilegedFields {
102                outcome: *outcome,
103                phase: *phase,
104                action,
105                target,
106                proposal_id,
107                principal,
108                counterparties,
109                approver_set_fingerprint,
110                key_id,
111                signature,
112                signature_verified: *signature_verified,
113                signed_at_ms: *signed_at_ms,
114                source,
115                reason,
116                time_ms: *time_ms,
117            },
118            product,
119        ),
120        AuditEvent::Lifecycle {
121            kind,
122            node_id,
123            time_ms,
124        } => ocsf_lifecycle(
125            *kind,
126            // `node_id` is the Kafka `broker.id` (an `int32` widened to `i64`
127            // at the broker boundary), always non-negative; wrap it into the
128            // canonical `u64` `NodeId`.
129            NodeId(u64::try_from(*node_id).unwrap_or(0)),
130            EpochMs(*time_ms),
131            product,
132        ),
133    }
134}