Skip to main content

endpoint_sec/event/
event_od_attribute_set.rs

1//! [`EventOdAttributeSet`]
2
3use std::ffi::OsStr;
4
5use endpoint_sec_sys::{es_event_od_attribute_set_t, es_od_record_type_t, es_string_token_t};
6
7use crate::{AuditToken, Process};
8
9/// Notification that an attribute is being set.
10///
11/// Attributes conceptually have the type `Map String (Set String)`.
12/// Each OD record has a Map of attribute name to Set of attribute value.
13/// When an attribute value is added, it is inserted into the set of values for that name.
14///
15/// The new set of attribute values may be empty.
16#[doc(alias = "es_event_od_attribute_set_t")]
17pub struct EventOdAttributeSet<'a> {
18    /// The raw reference.
19    pub(crate) raw: &'a es_event_od_attribute_set_t,
20    /// The version of the message.
21    pub(crate) version: u32,
22}
23
24impl<'a> EventOdAttributeSet<'a> {
25    /// Process that instigated operation (XPC caller).
26    #[inline(always)]
27    pub fn instigator(&self) -> Option<Process<'a>> {
28        // Safety: 'a tied to self, object obtained through ES
29        let process = unsafe { self.raw.instigator()? };
30        Some(Process::new(process, self.version))
31    }
32
33    /// Audit token of the process that instigated this event.
34    pub fn instigator_token(&self) -> AuditToken {
35        #[cfg(feature = "macos_15_0_0")]
36        if self.version >= 8 {
37            return AuditToken(self.raw.instigator_token);
38        }
39
40        // On old versions, the process was always non-null, and we can get
41        // its token easily.
42        self.instigator().unwrap().audit_token()
43    }
44
45    /// Result code for the operation.
46    #[inline(always)]
47    pub fn error_code(&self) -> i32 {
48        self.raw.error_code
49    }
50
51    /// The type of the record for which the attribute is being set.
52    #[inline(always)]
53    pub fn record_type(&self) -> es_od_record_type_t {
54        self.raw.record_type
55    }
56
57    /// The name of the record for which the attribute is being set.
58    #[inline(always)]
59    pub fn record_name(&self) -> &'a OsStr {
60        // Safety: 'a tied to self, object obtained through ES
61        unsafe { self.raw.record_name.as_os_str() }
62    }
63
64    /// The name of the attribute that was set.
65    #[inline(always)]
66    pub fn attribute_name(&self) -> &'a OsStr {
67        // Safety: 'a tied to self, object obtained through ES
68        unsafe { self.raw.attribute_name.as_os_str() }
69    }
70
71    /// The size of attribute_value_array.
72    #[inline(always)]
73    pub fn attribute_value_count(&self) -> usize {
74        self.raw.attribute_value_count
75    }
76
77    /// Iterator over the attribute values that were set.
78    #[inline(always)]
79    pub fn attribute_values<'s>(&'s self) -> AttributeValues<'s, 'a> {
80        AttributeValues::new(self)
81    }
82
83    /// OD node being mutated.
84    ///
85    /// Typically one of "/Local/Default", "/LDAPv3/<server>" or "/Active Directory/<domain>".
86    #[inline(always)]
87    pub fn node_name(&self) -> &'a OsStr {
88        // Safety: 'a tied to self, object obtained through ES
89        unsafe { self.raw.node_name.as_os_str() }
90    }
91
92    /// Optional. If node_name is "/Local/Default", this is, the path of the database against which
93    /// OD is authenticating.
94    #[inline(always)]
95    pub fn db_path(&self) -> Option<&'a OsStr> {
96        if self.node_name() == OsStr::new("/Local/Default") {
97            // Safety: 'a tied to self, object obtained through ES
98            Some(unsafe { self.raw.db_path.as_os_str() })
99        } else {
100            None
101        }
102    }
103}
104
105// Safety: safe to send across threads: does not contain any interior mutability nor depend on current thread state
106unsafe impl Send for EventOdAttributeSet<'_> {}
107// Safety: safe to share across threads: does not contain any interior mutability nor depend on current thread state
108unsafe impl Sync for EventOdAttributeSet<'_> {}
109
110impl_debug_eq_hash_with_functions!(EventOdAttributeSet<'a> with version; instigator, instigator_token, error_code, record_type, record_name, attribute_name, attribute_value_count, node_name, db_path);
111
112/// Read the `idx` attribute value of `raw`
113///
114/// # Safety
115///
116/// Must be called with a valid event for which `idx` is in range `0..raw.attribute_value_count`
117unsafe fn read_nth_attribute_value(raw: &es_event_od_attribute_set_t, idx: usize) -> es_string_token_t {
118    // SAFETY:
119    //  * upheld by the caller for the index;
120    //  * `raw.attribute_value_array` is given to us by ES, so adding to it preserves alignment;
121    unsafe { std::ptr::read(raw.attribute_value_array.add(idx)) }
122}
123
124make_event_data_iterator!(
125    EventOdAttributeSet;
126    /// Iterator over the attribute values of an [`EventOdAttributeSet`]
127    AttributeValues with attribute_value_count (usize);
128    &'raw OsStr;
129    read_nth_attribute_value,
130    super::as_os_str,
131);