sos_audit/
event.rs

1//! Audit trail event.
2use bitflags::bitflags;
3use serde::{Deserialize, Serialize};
4use sos_core::device::DevicePublicKey;
5use sos_core::events::{
6    AccountEvent, Event, EventKind, ReadEvent, WriteEvent,
7};
8use sos_core::{events::LogEvent, AccountId, SecretId, UtcDateTime, VaultId};
9
10bitflags! {
11    /// Bit flags for associated data.
12    pub struct AuditLogFlags: u16 {
13        /// Indicates whether associated data is present.
14        const DATA =        0b00000001;
15        /// Indicates the data has a vault identifier.
16        const DATA_VAULT =  0b00000010;
17        /// Indicates the data has a secret identifier.
18        const DATA_SECRET = 0b00000100;
19        /// Indicates the data has a move event.
20        const MOVE_SECRET = 0b00001000;
21        /// Indicates the data is for a device event.
22        const DEVICE = 0b00010000;
23    }
24}
25
26/// Audit log record.
27///
28/// An audit log record with no associated data is 36 bytes.
29///
30/// When associated data is available an additional 16 bytes is used
31/// for events on a vault and 32 bytes for events on a secret and for a
32/// move event 64 bytes is used.
33///
34/// The maximum size of a log record is thus 100 bytes.
35///
36/// * 2 bytes for bit flags.
37/// * 8 bytes for the timestamp seconds.
38/// * 4 bytes for the timestamp nanoseconds.
39/// * 2 bytes for the event kind identifier.
40/// * 20 bytes for the public account_id.
41/// * 16, 32 or 64 bytes for the context data (one, two or four UUIDs).
42#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
43#[serde(rename_all = "camelCase")]
44pub struct AuditEvent {
45    /// Time the event was created.
46    pub(crate) time: UtcDateTime,
47    /// Event being logged.
48    #[serde(rename = "type")]
49    pub(crate) event_kind: EventKind,
50    /// Account identifier of the client performing the event.
51    pub(crate) account_id: AccountId,
52    /// Context data about the event.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub(crate) data: Option<AuditData>,
55}
56
57impl AuditEvent {
58    /// Create a new audit log event.
59    pub fn new(
60        date_time: UtcDateTime,
61        event_kind: EventKind,
62        account_id: AccountId,
63        data: Option<AuditData>,
64    ) -> Self {
65        Self {
66            time: date_time,
67            event_kind,
68            account_id,
69            data,
70        }
71    }
72
73    /// Account identifier for this audit event.
74    pub fn account_id(&self) -> &AccountId {
75        &self.account_id
76    }
77
78    /// Date and time for this audit event.
79    pub fn time(&self) -> &UtcDateTime {
80        &self.time
81    }
82
83    /// Event kind for this audit event.
84    pub fn event_kind(&self) -> EventKind {
85        self.event_kind
86    }
87
88    /// Associated data for this audit event.
89    pub fn data(&self) -> Option<&AuditData> {
90        self.data.as_ref()
91    }
92
93    pub(crate) fn log_flags(&self) -> AuditLogFlags {
94        if let Some(data) = &self.data {
95            let mut flags = AuditLogFlags::empty();
96            flags.set(AuditLogFlags::DATA, true);
97            match data {
98                AuditData::Vault(_) => {
99                    flags.set(AuditLogFlags::DATA_VAULT, true);
100                }
101                AuditData::Secret(_, _) => {
102                    flags.set(AuditLogFlags::DATA_VAULT, true);
103                    flags.set(AuditLogFlags::DATA_SECRET, true);
104                }
105                AuditData::MoveSecret { .. } => {
106                    flags.set(AuditLogFlags::MOVE_SECRET, true);
107                }
108                AuditData::Device { .. } => {
109                    flags.set(AuditLogFlags::DEVICE, true);
110                }
111            }
112            flags
113        } else {
114            AuditLogFlags::empty()
115        }
116    }
117}
118
119impl From<(&AccountId, &Event)> for AuditEvent {
120    fn from(value: (&AccountId, &Event)) -> Self {
121        let (account_id, event) = value;
122        match event {
123            Event::CreateAccount(account_id) => AuditEvent::new(
124                Default::default(),
125                EventKind::CreateAccount,
126                *account_id,
127                None,
128            ),
129            Event::MoveSecret(_, _, _) => {
130                panic!("move secret audit event must be constructed")
131            }
132            Event::DeleteAccount(account_id) => AuditEvent::new(
133                Default::default(),
134                EventKind::DeleteAccount,
135                *account_id,
136                None,
137            ),
138            _ => {
139                let audit_data = match event {
140                    Event::Account(event) => {
141                        event.folder_id().map(AuditData::Vault)
142                    }
143                    Event::Folder(event, _) => {
144                        event.folder_id().map(AuditData::Vault)
145                    }
146                    Event::Read(vault_id, event) => match event {
147                        ReadEvent::ReadVault => {
148                            Some(AuditData::Vault(*vault_id))
149                        }
150                        ReadEvent::ReadSecret(secret_id) => {
151                            Some(AuditData::Secret(*vault_id, *secret_id))
152                        }
153                        ReadEvent::Noop => None,
154                    },
155                    Event::Write(vault_id, event) => match event {
156                        WriteEvent::CreateVault(_)
157                        | WriteEvent::SetVaultName(_)
158                        | WriteEvent::SetVaultFlags(_)
159                        | WriteEvent::SetVaultMeta(_) => {
160                            Some(AuditData::Vault(*vault_id))
161                        }
162                        WriteEvent::CreateSecret(secret_id, _) => {
163                            Some(AuditData::Secret(*vault_id, *secret_id))
164                        }
165                        WriteEvent::UpdateSecret(secret_id, _) => {
166                            Some(AuditData::Secret(*vault_id, *secret_id))
167                        }
168                        WriteEvent::DeleteSecret(secret_id) => {
169                            Some(AuditData::Secret(*vault_id, *secret_id))
170                        }
171                        WriteEvent::Noop => None,
172                    },
173                    _ => None,
174                };
175
176                if let Some(audit_data) = audit_data {
177                    AuditEvent::new(
178                        Default::default(),
179                        event.event_kind(),
180                        *account_id,
181                        Some(audit_data),
182                    )
183                } else {
184                    unreachable!("{:#?}", event);
185                }
186            }
187        }
188    }
189}
190
191impl From<(&AccountId, &AccountEvent)> for AuditEvent {
192    fn from(value: (&AccountId, &AccountEvent)) -> Self {
193        let (account_id, event) = value;
194        let audit_data = event.folder_id().map(AuditData::Vault);
195        AuditEvent::new(
196            Default::default(),
197            event.event_kind(),
198            *account_id,
199            audit_data,
200        )
201    }
202}
203
204/// Associated data for an audit log record.
205#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
206#[serde(rename_all = "camelCase")]
207pub enum AuditData {
208    /// Data for an associated vault.
209    Vault(VaultId),
210    /// Data for an associated secret.
211    Secret(VaultId, SecretId),
212    /// Data for a move secret event.
213    MoveSecret {
214        /// Moved from vault.
215        from_vault_id: VaultId,
216        /// Old secret identifier.
217        from_secret_id: SecretId,
218        /// Moved to vault.
219        to_vault_id: VaultId,
220        /// New secret identifier.
221        to_secret_id: SecretId,
222    },
223    /// Device trust or revoke events.
224    Device(DevicePublicKey),
225}
226
227impl Default for AuditData {
228    fn default() -> Self {
229        let zero = [0u8; 16];
230        Self::Vault(VaultId::from_bytes(zero))
231    }
232}