1use std::fmt;
5
6#[derive(Clone, PartialEq, Eq, Hash)]
8pub struct Sid {
9 pub revision: u8,
10 pub identifier_authority: u64,
12 pub sub_authorities: Vec<u32>,
13}
14
15impl Sid {
16 pub fn from_bytes(b: &[u8]) -> Option<Sid> {
18 if b.len() < 8 {
19 return None;
20 }
21 let revision = b[0];
22 let count = b[1] as usize;
23 let mut authority: u64 = 0;
24 for &byte in &b[2..8] {
25 authority = (authority << 8) | byte as u64; }
27 if b.len() < 8 + count * 4 {
28 return None;
29 }
30 let mut subs = Vec::with_capacity(count);
31 for i in 0..count {
32 let off = 8 + i * 4;
33 subs.push(u32::from_le_bytes([
34 b[off],
35 b[off + 1],
36 b[off + 2],
37 b[off + 3],
38 ]));
39 }
40 Some(Sid {
41 revision,
42 identifier_authority: authority,
43 sub_authorities: subs,
44 })
45 }
46
47 pub fn to_bytes(&self) -> Vec<u8> {
49 let mut b = vec![self.revision, self.sub_authorities.len() as u8];
50 let a = self.identifier_authority;
51 b.extend_from_slice(&[
52 (a >> 40) as u8,
53 (a >> 32) as u8,
54 (a >> 24) as u8,
55 (a >> 16) as u8,
56 (a >> 8) as u8,
57 a as u8,
58 ]);
59 for s in &self.sub_authorities {
60 b.extend_from_slice(&s.to_le_bytes());
61 }
62 b
63 }
64
65 pub fn parse(s: &str) -> Option<Sid> {
67 let mut it = s.split('-');
68 if it.next()? != "S" {
69 return None;
70 }
71 let revision = it.next()?.parse().ok()?;
72 let identifier_authority = it.next()?.parse().ok()?;
73 let sub_authorities = it.map(|p| p.parse().ok()).collect::<Option<Vec<u32>>>()?;
74 Some(Sid {
75 revision,
76 identifier_authority,
77 sub_authorities,
78 })
79 }
80
81 pub fn rid(&self) -> Option<u32> {
83 self.sub_authorities.last().copied()
84 }
85
86 pub fn is_well_known(&self) -> bool {
88 matches!(self.identifier_authority, 1 | 3) || (self.identifier_authority == 5 && self.sub_authorities.first() == Some(&32))
90 }
91}
92
93impl fmt::Display for Sid {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "S-{}-{}", self.revision, self.identifier_authority)?;
96 for s in &self.sub_authorities {
97 write!(f, "-{s}")?;
98 }
99 Ok(())
100 }
101}
102
103impl fmt::Debug for Sid {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write!(f, "{self}")
106 }
107}
108
109#[derive(Clone, Copy, PartialEq, Eq, Hash)]
112pub struct Guid(pub [u8; 16]);
113
114impl Guid {
115 pub fn from_bytes(b: &[u8]) -> Option<Guid> {
116 Some(Guid(b.get(..16)?.try_into().ok()?))
117 }
118
119 pub fn parse(s: &str) -> Option<Guid> {
121 let s = s.trim_matches(|c| c == '{' || c == '}');
122 let hex: String = s.chars().filter(|c| *c != '-').collect();
123 if hex.len() != 32 {
124 return None;
125 }
126 let raw: Vec<u8> = (0..16)
127 .map(|i| u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16).ok())
128 .collect::<Option<_>>()?;
129 let mut b = [0u8; 16];
131 b[0..4].copy_from_slice(&[raw[3], raw[2], raw[1], raw[0]]);
132 b[4..6].copy_from_slice(&[raw[5], raw[4]]);
133 b[6..8].copy_from_slice(&[raw[7], raw[6]]);
134 b[8..16].copy_from_slice(&raw[8..16]);
135 Some(Guid(b))
136 }
137}
138
139impl fmt::Display for Guid {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 let b = &self.0;
142 write!(
143 f,
144 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
145 b[3], b[2], b[1], b[0], b[5], b[4], b[7], b[6],
146 b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]
147 )
148 }
149}
150
151impl fmt::Debug for Guid {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "{self}")
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn sid_roundtrip_string_and_bytes() {
163 let s = Sid::parse("S-1-5-21-1-2-3-1104").unwrap();
164 assert_eq!(s.rid(), Some(1104));
165 assert_eq!(s.to_string(), "S-1-5-21-1-2-3-1104");
166 let bytes = s.to_bytes();
167 assert_eq!(Sid::from_bytes(&bytes).unwrap(), s);
168 }
169
170 #[test]
171 fn guid_parse_display_roundtrip() {
172 let g = Guid::parse("1131f6aa-9c07-11d1-f79f-00c04fc2dcd2").unwrap();
173 assert_eq!(g.to_string(), "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2");
174 }
175}