11from __future__ import annotations
22
3+ import struct
34from functools import cached_property
45from typing import TYPE_CHECKING , Any , ClassVar
56
89
910if TYPE_CHECKING :
1011 from collections .abc import Iterator
12+ from datetime import datetime
1113
1214 from dissect .database .ese .ntds .database import Database
13- from dissect .database .ese .ntds .sd import ACL , SecurityDescriptor
15+ from dissect .database .ese .ntds .sd import SecurityDescriptor
1416 from dissect .database .ese .record import Record
1517
1618
@@ -78,11 +80,63 @@ def as_dict(self) -> dict[str, Any]:
7880
7981 return result
8082
83+ def parent (self ) -> Object | None :
84+ """Return the parent object of this object, if any."""
85+ if (pdnt := self .get ("Pdnt" )) is not None :
86+ return self .db .data ._lookup_dnt (pdnt )
87+ return None
88+
89+ def ancestors (self ) -> Iterator [Object ]:
90+ """Yield all ancestor objects of this object."""
91+ for (dnt ,) in list (struct .iter_unpack ("<I" , self .get ("Ancestors" )))[::- 1 ]:
92+ yield self .db .data ._lookup_dnt (dnt )
93+
94+ def children (self ) -> Iterator [Object ]:
95+ """Yield all child objects of this object."""
96+ # Our query code currently doesn't really work nicely on this specific query, so just do it manually for now
97+ cursor = self .db .data .table .index ("PDNT_index" ).cursor ()
98+ cursor .seek (PDNT_col = self .DNT + 1 )
99+ end = cursor .record ()
100+
101+ cursor .reset ()
102+ cursor .seek (PDNT_col = self .DNT )
103+
104+ record = cursor .record ()
105+ while record != end :
106+ yield Object .from_record (self .db , record )
107+ record = cursor .next ()
108+
109+ # Some commonly used properties, for convenience and type hinting
110+ @property
111+ def dnt (self ) -> int :
112+ """Return the object's Directory Number Tag (DNT)."""
113+ return self .get ("DNT" )
114+
115+ @property
116+ def pdnt (self ) -> int | None :
117+ """Return the object's Parent Directory Number Tag (PDNT)."""
118+ return self .get ("Pdnt" )
119+
120+ @property
121+ def ncdnt (self ) -> int | None :
122+ """Return the object's Naming Context Directory Number Tag (NCDNT)."""
123+ return self .get ("NCDNT" )
124+
125+ @property
126+ def name (self ) -> str | None :
127+ """Return the object's name."""
128+ return self .get ("name" )
129+
81130 @property
82131 def sid (self ) -> str | None :
83132 """Return the object's Security Identifier (SID)."""
84133 return self .get ("objectSid" )
85134
135+ @property
136+ def guid (self ) -> str | None :
137+ """Return the object's GUID."""
138+ return self .get ("objectGUID" )
139+
86140 @property
87141 def distinguishedName (self ) -> str | None :
88142 """Return the fully qualified Distinguished Name (DN) for this object."""
@@ -100,18 +154,122 @@ def sd(self) -> SecurityDescriptor | None:
100154 return None
101155
102156 @property
103- def sacl (self ) -> ACL | None :
104- """Return the System Access Control List (SACL) for this object."""
105- if (sd := self .sd ) is not None :
106- return sd .sacl
107- return None
157+ def when_created (self ) -> datetime | None :
158+ """Return the object's creation time."""
159+ return self .get ("whenCreated" )
108160
109161 @property
110- def dacl (self ) -> ACL | None :
111- """Return the Discretionary Access Control List (DACL) for this object."""
112- if (sd := self .sd ) is not None :
113- return sd .dacl
114- return None
162+ def when_changed (self ) -> datetime | None :
163+ """Return the object's last modification time."""
164+ return self .get ("whenChanged" )
165+
166+
167+ class Domain (Object ):
168+ """Represents a domain object in the Active Directory."""
169+
170+ __object_class__ = "domain"
171+
172+ def __repr__ (self ) -> str :
173+ return f"<Domain name={ self .name !r} >"
174+
175+
176+ class DomainDNS (Domain ):
177+ """Represents a domain DNS object in the Active Directory."""
178+
179+ __object_class__ = "domainDNS"
180+
181+ def __repr__ (self ) -> str :
182+ return f"<DomainDNS name={ self .name !r} >"
183+
184+
185+ class BuiltinDomain (Object ):
186+ """Represents a built-in domain object in the Active Directory."""
187+
188+ __object_class__ = "builtinDomain"
189+
190+ def __repr__ (self ) -> str :
191+ return f"<BuiltinDomain name={ self .name !r} >"
192+
193+
194+ class Configuration (Object ):
195+ """Represents a configuration object in the Active Directory."""
196+
197+ __object_class__ = "configuration"
198+
199+ def __repr__ (self ) -> str :
200+ return f"<Configuration name={ self .name !r} >"
201+
202+
203+ class QuotaContainer (Object ):
204+ """Represents a quota container object in the Active Directory."""
205+
206+ __object_class__ = "msDS-QuotaContainer"
207+
208+ def __repr__ (self ) -> str :
209+ return f"<QuotaContainer name={ self .name !r} >"
210+
211+
212+ class CrossRefContainer (Object ):
213+ """Represents a cross-reference container object in the Active Directory."""
214+
215+ __object_class__ = "crossRefContainer"
216+
217+ def __repr__ (self ) -> str :
218+ return f"<CrossRefContainer name={ self .name !r} >"
219+
220+
221+ class SitesContainer (Object ):
222+ """Represents a sites container object in the Active Directory."""
223+
224+ __object_class__ = "sitesContainer"
225+
226+ def __repr__ (self ) -> str :
227+ return f"<SitesContainer name={ self .name !r} >"
228+
229+
230+ class Locality (Object ):
231+ """Represents a locality object in the Active Directory."""
232+
233+ __object_class__ = "locality"
234+
235+ def __repr__ (self ) -> str :
236+ return f"<Locality name={ self .name !r} >"
237+
238+
239+ class PhysicalLocation (Object ):
240+ """Represents a physical location object in the Active Directory."""
241+
242+ __object_class__ = "physicalLocation"
243+
244+ def __repr__ (self ) -> str :
245+ return f"<PhysicalLocation name={ self .name !r} >"
246+
247+
248+ class Container (Object ):
249+ """Represents a container object in the Active Directory."""
250+
251+ __object_class__ = "container"
252+
253+ def __repr__ (self ) -> str :
254+ return f"<Container name={ self .name !r} >"
255+
256+
257+ class OrganizationalUnit (Object ):
258+ """Represents an organizational unit object in the Active Directory."""
259+
260+ __object_class__ = "organizationalUnit"
261+
262+ def __repr__ (self ) -> str :
263+ return f"<OrganizationalUnit name={ self .name !r} >"
264+
265+
266+ class LostAndFound (Object ):
267+ """Represents a lost and found object in the Active Directory."""
268+
269+ __object_class__ = "lostAndFound"
270+
271+ def __repr__ (self ) -> str :
272+ return f"<LostAndFound name={ self .name !r} >"
115273
116274
117275class Group (Object ):
0 commit comments