1use serde::{Deserialize, Serialize};
2use serde_json::value::Value;
3use ldap3::SearchEntry;
4use log::{debug, trace};
5use std::collections::HashMap;
6use std::error::Error;
7
8use crate::enums::{decode_guid_le, group_scope};
9use crate::enums::regex::OBJECT_SID_RE1;
10use crate::objects::common::{LdapObject, AceTemplate, SPNTarget, Link, Member};
11use crate::enums::acl::parse_ntsecuritydescriptor;
12use crate::enums::secdesc::LdapSid;
13use crate::enums::sid::{objectsid_to_vec8, sid_maker};
14use crate::utils::date::string_to_epoch;
15
16#[derive(Debug, Clone, Deserialize, Serialize, Default)]
18pub struct Group {
19 #[serde(rename = "ObjectIdentifier")]
20 object_identifier: String,
21 #[serde(rename = "IsDeleted")]
22 is_deleted: bool,
23 #[serde(rename = "IsACLProtected")]
24 is_acl_protected: bool,
25 #[serde(rename = "Properties")]
26 properties: GroupProperties,
27 #[serde(rename = "Members")]
28 members: Vec<Member>,
29 #[serde(rename = "HasSIDHistory")]
30 has_sid_history: Vec<String>,
31 #[serde(rename = "Aces")]
32 aces: Vec<AceTemplate>,
33 #[serde(rename = "ContainedBy")]
34 contained_by: Option<Member>,
35}
36
37impl Group {
38 pub fn new() -> Self {
40 Self { ..Default::default() }
41 }
42
43 pub fn members(&self) -> &Vec<Member> {
45 &self.members
46 }
47 pub fn properties(&self) -> &GroupProperties {
48 &self.properties
49 }
50 pub fn object_identifier(&self) -> &String {
51 &self.object_identifier
52 }
53
54 pub fn properties_mut(&mut self) -> &mut GroupProperties {
56 &mut self.properties
57 }
58 pub fn object_identifier_mut(&mut self) -> &mut String {
59 &mut self.object_identifier
60 }
61 pub fn members_mut(&mut self) -> &mut Vec<Member> {
62 &mut self.members
63 }
64
65 pub fn parse(
68 &mut self,
69 result: SearchEntry,
70 domain: &str,
71 dn_sid: &mut HashMap<String, String>,
72 sid_type: &mut HashMap<String, String>,
73 domain_sid: &str,
74 schema_guid_map: &HashMap<String, String>,
75 ) -> Result<(), Box<dyn Error>> {
76 let result_dn: String = result.dn.to_uppercase();
77 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
78 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
79
80 debug!("Parse group: {result_dn}");
81
82 for (key, value) in &result_attrs {
84 trace!(" {key:?}:{value:?}");
85 }
86 for (key, value) in &result_bin {
88 trace!(" {key:?}:{value:?}");
89 }
90
91 self.properties.domain = domain.to_uppercase();
93 self.properties.distinguishedname = result_dn;
94 self.properties.domainsid = domain_sid.to_string();
95
96 for (key, value) in &result_attrs {
98 match key.as_str() {
99 "name" => {
100 let name = &value[0];
101 let email = format!("{}@{}", name.to_owned(), domain);
102 self.properties.name = email.to_uppercase();
103 }
104 "description" => {
105 self.properties.description = Some(value[0].to_owned());
106 }
107 "adminCount" => {
108 let admincount = value[0].parse::<i32>().unwrap_or(0) != 0;
112 self.properties.admincount = admincount;
113 self.properties.adminsdholderprotected = admincount;
114 }
115 "groupType" => {
116 self.properties.groupscope = group_scope(value[0].parse::<i64>().unwrap_or(0));
117 }
118 "sAMAccountName" => {
119 self.properties.samaccountname = value[0].to_owned();
120 }
121 "member" => {
122 if !value.is_empty() {
123 let mut _vec_members: Vec<Member> = Vec::new();
124
125 for member in value {
126 let _member = member.trim();
127 if _member.is_empty() {
128 continue;
129 }
130 if _member.eq_ignore_ascii_case("SID") {
131 continue;
132 }
133
134 let mut m = Member::new();
135 *m.object_identifier_mut() = _member.to_uppercase();
136 _vec_members.push(m);
137 }
138
139 self.members = _vec_members;
140 }
141 }
142 "objectSid" => {
143 let vec_sid = objectsid_to_vec8(&value[0]);
145 let sid = sid_maker(LdapSid::parse(&vec_sid).unwrap().1, domain);
146 self.object_identifier = sid.to_owned();
147
148 if sid.ends_with("-512")
156 || sid.ends_with("-516")
157 || sid.ends_with("-519")
158 || sid.ends_with("-520")
159 {
160 self.properties.highvalue = true;
161 } else if sid.ends_with("S-1-5-32-544")
162 || sid.ends_with("S-1-5-32-548")
163 || sid.ends_with("S-1-5-32-549")
164 || sid.ends_with("S-1-5-32-550")
165 || sid.ends_with("S-1-5-32-551")
166 {
167 self.properties.highvalue = true;
168 } else {
169 self.properties.highvalue = false;
170 }
171 }
172 "whenCreated" => {
173 let epoch = string_to_epoch(&value[0])?;
174 if epoch.is_positive() {
175 self.properties.whencreated = epoch;
176 }
177 }
178 "isDeleted" => {
179 self.is_deleted = true;
180 }
181 _ => {}
182 }
183 }
184
185 for (key, value) in &result_bin {
187 match key.as_str() {
188 "objectGUID" => {
189 let guid = decode_guid_le(&value[0]);
191 self.properties.objectguid = guid;
192 }
193 "objectSid" => {
194 let sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
196 self.object_identifier = sid.to_owned();
197
198 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
199 self.properties.domainsid = domain_sid[0].to_owned().to_string();
200 }
201
202 if sid.ends_with("-512")
204 || sid.ends_with("-516")
205 || sid.ends_with("-519")
206 || sid.ends_with("-520")
207 {
208 self.properties.highvalue = true;
209 }
210 else if sid.ends_with("S-1-5-32-544")
211 || sid.ends_with("S-1-5-32-548")
212 || sid.ends_with("S-1-5-32-549")
213 || sid.ends_with("S-1-5-32-550")
214 || sid.ends_with("S-1-5-32-551")
215 {
216 self.properties.highvalue = true;
217 }
218 else {
219 self.properties.highvalue = false;
220 }
221 }
222 "nTSecurityDescriptor" => {
223 let relations_ace = parse_ntsecuritydescriptor(
225 self,
226 &value[0],
227 "Group",
228 &result_attrs,
229 &result_bin,
230 domain,
231 schema_guid_map,
232 );
233 self.aces = relations_ace;
234 }
235 "sIDHistory" => {
236 let mut list_sid_history: Vec<String> = Vec::new();
237 for bsid in value {
238 debug!("sIDHistory: {:?}", &bsid);
239 list_sid_history.push(sid_maker(LdapSid::parse(bsid).unwrap().1, domain));
240 }
241 self.properties.sidhistory = list_sid_history.clone();
242 self.has_sid_history = list_sid_history;
243 }
244 _ => {}
245 }
246 }
247
248 dn_sid.insert(
250 self.properties.distinguishedname.to_string(),
251 self.object_identifier.to_string(),
252 );
253 sid_type.insert(
255 self.object_identifier.to_string(),
256 "Group".to_string(),
257 );
258
259 Ok(())
262 }
263}
264
265impl LdapObject for Group {
266 fn to_json(&self) -> Value {
268 serde_json::to_value(self).unwrap()
269 }
270
271 fn get_object_identifier(&self) -> &String {
273 &self.object_identifier
274 }
275 fn get_is_acl_protected(&self) -> &bool {
276 &self.is_acl_protected
277 }
278 fn get_aces(&self) -> &Vec<AceTemplate> {
279 &self.aces
280 }
281 fn get_spntargets(&self) -> &Vec<SPNTarget> {
282 panic!("Not used by current object.");
283 }
284 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
285 panic!("Not used by current object.");
286 }
287 fn get_links(&self) -> &Vec<Link> {
288 panic!("Not used by current object.");
289 }
290 fn get_contained_by(&self) -> &Option<Member> {
291 &self.contained_by
292 }
293 fn get_child_objects(&self) -> &Vec<Member> {
294 panic!("Not used by current object.");
295 }
296 fn get_haslaps(&self) -> &bool {
297 &false
298 }
299
300 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
302 &mut self.aces
303 }
304 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
305 panic!("Not used by current object.");
306 }
307 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
308 panic!("Not used by current object.");
309 }
310
311 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
313 self.is_acl_protected = is_acl_protected;
314 self.properties.isaclprotected = is_acl_protected;
315 }
316 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
317 self.aces = aces;
318 }
319 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
320 }
322 fn set_allowed_to_delegate(&mut self, _allowed_to_delegate: Vec<Member>) {
323 }
325 fn set_links(&mut self, _links: Vec<Link>) {
326 }
328 fn set_contained_by(&mut self, contained_by: Option<Member>) {
329 self.contained_by = contained_by;
330 }
331 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
332 }
334 fn set_owner_rights_flags(&mut self, any: bool, any_inherited: bool) {
335 self.properties.doesanyacegrantownerrights = any;
336 self.properties.doesanyinheritedacegrantownerrights = any_inherited;
337 }
338}
339
340#[derive(Debug, Clone, Deserialize, Serialize, Default)]
342pub struct GroupProperties {
343 domain: String,
344 name: String,
345 distinguishedname: String,
346 domainsid: String,
347 objectguid: String,
348 doesanyacegrantownerrights: bool,
349 doesanyinheritedacegrantownerrights: bool,
350 isaclprotected: bool,
351 highvalue: bool,
352 samaccountname: String,
353 description: Option<String>,
354 whencreated: i64,
355 admincount: bool,
356 adminsdholderprotected: bool,
357 groupscope: String,
358 sidhistory: Vec<String>,
359}
360
361impl GroupProperties {
362 pub fn name(&self) -> &String {
364 &self.name
365 }
366
367 pub fn name_mut(&mut self) -> &mut String {
369 &mut self.name
370 }
371 pub fn domain_mut(&mut self) -> &mut String {
372 &mut self.domain
373 }
374 pub fn domainsid_mut(&mut self) -> &mut String {
375 &mut self.domainsid
376 }
377 pub fn highvalue_mut(&mut self) -> &mut bool {
378 &mut self.highvalue
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 #[test]
387 fn group_type_maps_to_bloodhound_scope() {
388 assert_eq!(group_scope(-2147483646), "Global"); assert_eq!(group_scope(-2147483643), "DomainLocal"); assert_eq!(group_scope(-2147483644), "DomainLocal"); assert_eq!(group_scope(-2147483640), "Universal"); assert_eq!(group_scope(2), "Global"); assert_eq!(group_scope(8), "Universal"); assert_eq!(group_scope(0), "");
396 }
397}