@@ -155,6 +155,146 @@ public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups_Null_Gpcfilesyspath(
155155 Assert . Equal ( "teapot" , actual . ObjectIdentifier ) ;
156156 }
157157
158+ [ Fact ]
159+ public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups_Does_Not_Skip_Enabled_And_Skips_Disabled_GPOs ( ) {
160+ // Setup
161+ var mockLDAPUtils = new Mock < ILdapUtils > ( MockBehavior . Loose ) ;
162+ var gpcFileSysPath = Path . GetTempPath ( ) ;
163+
164+ var groupsXmlPath = Path . Join ( gpcFileSysPath , "MACHINE" , "Preferences" , "Groups" , "Groups.xml" ) ;
165+ Directory . CreateDirectory ( Path . GetDirectoryName ( groupsXmlPath ) ) ;
166+ await File . WriteAllTextAsync ( groupsXmlPath , GroupXmlContent ) ;
167+
168+ var gptTmplPath = Path . Join ( gpcFileSysPath , "MACHINE" , "Microsoft" , "Windows NT" , "SecEdit" , "GptTmpl.inf" ) ;
169+ Directory . CreateDirectory ( Path . GetDirectoryName ( gptTmplPath ) ) ;
170+ await File . WriteAllTextAsync ( gptTmplPath , GpttmplInfContent ) ;
171+
172+ var mockComputerEntry = new Mock < IDirectoryObject > ( ) ;
173+ var mockSearchResultEntry = new Mock < IDirectoryObject > ( ) ;
174+ var sid = "teapot" ;
175+ mockSearchResultEntry . Setup ( x => x . TryGetSecurityIdentifier ( out sid ) ) . Returns ( true ) ;
176+ var mockResult = LdapResult < IDirectoryObject > . Ok ( mockSearchResultEntry . Object ) ;
177+ var mockSearchResults = new List < LdapResult < IDirectoryObject > > { mockResult } ;
178+ mockLDAPUtils
179+ . Setup ( x => x . Query (
180+ It . Is < LdapQueryParameters > ( y =>
181+ y . LDAPFilter . Equals ( new LdapFilter ( ) . AddComputersNoMSAs ( ) . GetFilter ( ) ) &&
182+ y . Attributes . Equals ( CommonProperties . ObjectSID ) ) ,
183+ It . IsAny < CancellationToken > ( ) ) ) . Returns ( mockSearchResults . ToAsyncEnumerable ) ;
184+ mockComputerEntry . Setup ( x => x . TryGetSecurityIdentifier ( out sid ) ) . Returns ( true ) ;
185+ mockLDAPUtils . Setup ( x => x . ResolveAccountName ( It . IsAny < string > ( ) , It . IsAny < string > ( ) ) )
186+ . ReturnsAsync ( ( true , new TypedPrincipal ( "S-1-5-21-3130019616-2776909439-2417379446-513" , Label . User ) ) ) ;
187+
188+ // Enabled
189+ var mockDirectory0 = new MockDirectoryObject ( "CN=Users,DC=testlab,DC=local" , null , "" ,
190+ "ECAD920E-8EB1-4E31-A80E-DD36367F81F4" ) ;
191+ mockDirectory0 . Properties = new Dictionary < string , string > ( ) {
192+ { LDAPProperties . GPCFileSYSPath , gpcFileSysPath } ,
193+ { LDAPProperties . Flags , "0" }
194+ } ;
195+ var result0 = new List < LdapResult < IDirectoryObject > > {
196+ LdapResult < IDirectoryObject > . Ok ( mockDirectory0 ) ,
197+ } ;
198+ // User Configuration Disabled
199+ var mockDirectory1 = new MockDirectoryObject ( "CN=Users,DC=testlab,DC=local" , null , "" ,
200+ "ECAD920E-8EB1-4E31-A80E-DD36367F81F4" ) ;
201+ mockDirectory1 . Properties = new Dictionary < string , string > ( ) {
202+ { LDAPProperties . GPCFileSYSPath , gpcFileSysPath } ,
203+ { LDAPProperties . Flags , "1" }
204+ } ;
205+ var result1 = new List < LdapResult < IDirectoryObject > > {
206+ LdapResult < IDirectoryObject > . Ok ( mockDirectory1 ) ,
207+ } ;
208+ // Computer Configuration Disabled -- Skipped
209+ var mockDirectory2 = new MockDirectoryObject ( "CN=Users,DC=testlab,DC=local" , null , "" ,
210+ "ECAD920E-8EB1-4E31-A80E-DD36367F81F4" ) ;
211+ mockDirectory2 . Properties = new Dictionary < string , string > ( ) {
212+ { LDAPProperties . GPCFileSYSPath , gpcFileSysPath } ,
213+ { LDAPProperties . Flags , "2" }
214+ } ;
215+ var result2 = new List < LdapResult < IDirectoryObject > > {
216+ LdapResult < IDirectoryObject > . Ok ( mockDirectory2 ) ,
217+ } ;
218+ // Disabled -- Skipped
219+ var mockDirectory3 = new MockDirectoryObject ( "CN=Users,DC=testlab,DC=local" , null , "" ,
220+ "ECAD920E-8EB1-4E31-A80E-DD36367F81F4" ) ;
221+ mockDirectory3 . Properties = new Dictionary < string , string > ( ) {
222+ { LDAPProperties . GPCFileSYSPath , gpcFileSysPath } ,
223+ { LDAPProperties . Flags , "3" }
224+ } ;
225+ var result3 = new List < LdapResult < IDirectoryObject > > {
226+ LdapResult < IDirectoryObject > . Ok ( mockDirectory3 ) ,
227+ } ;
228+
229+ mockLDAPUtils
230+ . Setup ( x => x . Query (
231+ It . Is < LdapQueryParameters > ( y =>
232+ y . LDAPFilter . Equals ( new LdapFilter ( ) . AddAllObjects ( ) . GetFilter ( ) ) &&
233+ y . SearchScope . Equals ( SearchScope . Base ) &&
234+ y . Attributes . Contains ( LDAPProperties . GPCFileSYSPath ) &&
235+ y . Attributes . Contains ( LDAPProperties . Flags ) &&
236+ y . SearchBase . Equals ( "cn=foouser (blah)123/dc=somedomain" , StringComparison . OrdinalIgnoreCase ) &&
237+ y . DomainName . Equals ( "somedomain" , StringComparison . OrdinalIgnoreCase ) ) ,
238+ It . IsAny < CancellationToken > ( ) ) )
239+ . Returns ( result0 . ToAsyncEnumerable ) ;
240+ mockLDAPUtils
241+ . Setup ( x => x . Query (
242+ It . Is < LdapQueryParameters > ( y =>
243+ y . LDAPFilter . Equals ( new LdapFilter ( ) . AddAllObjects ( ) . GetFilter ( ) ) &&
244+ y . SearchScope . Equals ( SearchScope . Base ) &&
245+ y . Attributes . Contains ( LDAPProperties . GPCFileSYSPath ) &&
246+ y . Attributes . Contains ( LDAPProperties . Flags ) &&
247+ y . SearchBase . Equals ( "cn=foouser (blah)123/dc=someotherdomain" , StringComparison . OrdinalIgnoreCase ) &&
248+ y . DomainName . Equals ( "someotherdomain" , StringComparison . OrdinalIgnoreCase ) ) ,
249+ It . IsAny < CancellationToken > ( ) ) )
250+ . Returns ( result1 . ToAsyncEnumerable ) ;
251+ mockLDAPUtils
252+ . Setup ( x => x . Query (
253+ It . Is < LdapQueryParameters > ( y =>
254+ y . LDAPFilter . Equals ( new LdapFilter ( ) . AddAllObjects ( ) . GetFilter ( ) ) &&
255+ y . SearchScope . Equals ( SearchScope . Base ) &&
256+ y . Attributes . Contains ( LDAPProperties . GPCFileSYSPath ) &&
257+ y . Attributes . Contains ( LDAPProperties . Flags ) &&
258+ y . SearchBase . Equals ( "cn=foouser (blah)123/dc=somethirddomain" , StringComparison . OrdinalIgnoreCase ) &&
259+ y . DomainName . Equals ( "somethirddomain" , StringComparison . OrdinalIgnoreCase ) ) ,
260+ It . IsAny < CancellationToken > ( ) ) )
261+ . Returns ( result2 . ToAsyncEnumerable ) ;
262+ mockLDAPUtils
263+ . Setup ( x => x . Query (
264+ It . Is < LdapQueryParameters > ( y =>
265+ y . LDAPFilter . Equals ( new LdapFilter ( ) . AddAllObjects ( ) . GetFilter ( ) ) &&
266+ y . SearchScope . Equals ( SearchScope . Base ) &&
267+ y . Attributes . Contains ( LDAPProperties . GPCFileSYSPath ) &&
268+ y . Attributes . Contains ( LDAPProperties . Flags ) &&
269+ y . SearchBase . Equals ( "cn=foouser (blah)123/dc=somefourthdomain" , StringComparison . OrdinalIgnoreCase ) &&
270+ y . DomainName . Equals ( "somefourthdomain" , StringComparison . OrdinalIgnoreCase ) ) ,
271+ It . IsAny < CancellationToken > ( ) ) )
272+ . Returns ( result3 . ToAsyncEnumerable ) ;
273+
274+ var processor = new GPOLocalGroupProcessor ( mockLDAPUtils . Object ) ;
275+ var testGPLinkProperty0 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somedomain;0;]" ;
276+ var testGPLinkProperty1 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=someotherdomain;0;]" ;
277+ var testGPLinkProperty2 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somethirddomain;0;]" ;
278+ var testGPLinkProperty3 = "[LDAP:/o=foo/ou=foo Group (ABC123)/cn=foouser (blah)123/dc=somefourthdomain;0;]" ;
279+
280+ // Act
281+ var act0 = await processor . ReadGPOLocalGroups ( testGPLinkProperty0 , "DC=Testlab,DC=Local" ) ;
282+ var act1 = await processor . ReadGPOLocalGroups ( testGPLinkProperty1 , "DC=Testlab,DC=Local" ) ;
283+ var act2 = await processor . ReadGPOLocalGroups ( testGPLinkProperty2 , "DC=Testlab,DC=Local" ) ;
284+ var act3 = await processor . ReadGPOLocalGroups ( testGPLinkProperty3 , "DC=Testlab,DC=Local" ) ;
285+
286+ // Assert
287+ Assert . Single ( act0 . AffectedComputers ) ;
288+ Assert . Single ( act1 . AffectedComputers ) ;
289+ Assert . Single ( act2 . AffectedComputers ) ;
290+ Assert . Single ( act3 . AffectedComputers ) ;
291+
292+ Assert . Single ( act0 . LocalAdmins ) ;
293+ Assert . Single ( act1 . LocalAdmins ) ;
294+ Assert . Empty ( act2 . LocalAdmins ) ;
295+ Assert . Empty ( act3 . LocalAdmins ) ;
296+ }
297+
158298 [ WindowsOnlyFact ]
159299 public async Task GPOLocalGroupProcessor_ReadGPOLocalGroups ( ) {
160300 var mockLDAPUtils = new Mock < ILdapUtils > ( MockBehavior . Loose ) ;
0 commit comments