forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First DirectoryServcies tests which runs against real server (dotnet/…
…corefx#24316) * First DirectoryServcies tests which runs against real server * Fix msbuild failure with None element * Address the feedback * Fix some formatting Commit migrated from dotnet/corefx@f4c2ba6
- Loading branch information
Showing
6 changed files
with
314 additions
and
5 deletions.
There are no files selected for viewing
117 changes: 117 additions & 0 deletions
117
src/libraries/Common/tests/System/DirectoryServices/LdapConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System.IO; | ||
using System.Xml.Linq; | ||
|
||
namespace System.DirectoryServices.Tests | ||
{ | ||
internal class LdapConfiguration | ||
{ | ||
private LdapConfiguration(string serverName, string domain, string userName, string password, string port, AuthenticationTypes at) | ||
{ | ||
ServerName = serverName; | ||
Domain = domain; | ||
UserName = userName; | ||
Password = password; | ||
Port = port; | ||
AuthenticationTypes = at; | ||
} | ||
|
||
private static LdapConfiguration s_ldapConfiguration = GetConfiguration("LDAP.Configuration.xml"); | ||
|
||
internal static LdapConfiguration Configuration => s_ldapConfiguration; | ||
|
||
internal string ServerName { get; set; } | ||
internal string UserName { get; set; } | ||
internal string Password { get; set; } | ||
internal string Port { get; set; } | ||
internal string Domain { get; set; } | ||
internal AuthenticationTypes AuthenticationTypes { get; set; } | ||
internal string LdapPath => String.IsNullOrEmpty(Port) ? $"LDAP://{ServerName}/{Domain}" : $"LDAP://{ServerName}:{Port}/{Domain}"; | ||
|
||
internal string GetLdapPath(string prefix) // like "ou=something" | ||
{ | ||
return String.IsNullOrEmpty(Port) ? $"LDAP://{ServerName}/{prefix},{Domain}" : $"LDAP://{ServerName}:{Port}/{prefix},{Domain}"; | ||
} | ||
|
||
internal static LdapConfiguration GetConfiguration(string configFile) | ||
{ | ||
if (!File.Exists(configFile)) | ||
return null; | ||
|
||
LdapConfiguration ldapConfig = null; | ||
try | ||
{ | ||
string serverName = ""; | ||
string domain = ""; | ||
string port = ""; | ||
string user = ""; | ||
string password = ""; | ||
AuthenticationTypes at = AuthenticationTypes.None; | ||
|
||
XElement config = XDocument.Load(configFile).Element("Configuration"); | ||
if (config != null) | ||
{ | ||
XElement child = config.Element("ServerName"); | ||
if (child != null) | ||
serverName = child.Value; | ||
|
||
child = config.Element("Domain"); | ||
if (child != null) | ||
domain = child.Value; | ||
|
||
child = config.Element("Port"); | ||
if (child != null) | ||
port = child.Value; | ||
|
||
child = config.Element("User"); | ||
if (child != null) | ||
user = child.Value; | ||
|
||
child = config.Element("Password"); | ||
if (child != null) | ||
password = child.Value; | ||
|
||
child = config.Element("AuthenticationTypes"); | ||
if (child != null) | ||
{ | ||
string[] parts = child.Value.Split(','); | ||
foreach (string p in parts) | ||
{ | ||
string s = p.Trim(); | ||
if (s.Equals("Anonymous", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.Anonymous; | ||
if (s.Equals("Delegation", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.Delegation; | ||
if (s.Equals("Encryption", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.FastBind; | ||
if (s.Equals("FastBind", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.FastBind; | ||
if (s.Equals("ReadonlyServer", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.ReadonlyServer; | ||
if (s.Equals("Sealing", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.Sealing; | ||
if (s.Equals("Secure", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.Secure; | ||
if (s.Equals("SecureSocketsLayer", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.SecureSocketsLayer; | ||
if (s.Equals("ServerBind", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.ServerBind; | ||
if (s.Equals("Signing", StringComparison.OrdinalIgnoreCase)) | ||
at |= AuthenticationTypes.Signing; | ||
} | ||
} | ||
|
||
ldapConfig = new LdapConfiguration(serverName, domain, user, password, port, at); | ||
} | ||
} | ||
catch | ||
{ | ||
// Couldn't read the configurations, usually we'll skip the tests which depend on that | ||
} | ||
return ldapConfig; | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/libraries/System.DirectoryServices/tests/LDAP.Configuration.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
To enable the tests marked with [ConditionalFact(nameof(IsLdapConfigurationExist))], need to setup LDAP server and provide the needed server info here. | ||
The easiest way to get LDAP server is to download and install OpenDJ https://backstage.forgerock.com/downloads/OpenDJ/Directory%20Services/5.0.0#browse. | ||
Download "DS zip" file then extract it. you can set it up by running a command like that following: | ||
/setup directory-server --sampleData 2 --rootUserDN "cn=Directory Manager" --rootUserPassword password --hostname localhost.localdomain --ldapPort 1389 --ldapsPort 1636 --httpPort 8080 --httpsPort 8443 --adminConnectorPort 4444 --baseDN dc=example,dc=com --acceptLicense –enableStartTls | ||
After that delete this comment and have the following contents in this Xml file: | ||
<Configuration> | ||
<ServerName>server machine name</ServerName> | ||
<Domain>DC=example,DC=com</Domain> | ||
<Port>1389</Port> | ||
<User>cn=Directory Manager</User> | ||
<Password>password</Password> | ||
<AuthenticationTypes>ServerBind,None</AuthenticationTypes> | ||
</Configuration> | ||
--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
...braries/System.DirectoryServices/tests/System/DirectoryServices/DirectoryServicesTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System.Runtime.InteropServices; | ||
using System.Collections.Generic; | ||
using System.Collections; | ||
using Xunit; | ||
|
||
namespace System.DirectoryServices.Tests | ||
{ | ||
public class DirectoryServicesTests | ||
{ | ||
internal static bool IsLdapConfigurationExist => LdapConfiguration.Configuration != null; | ||
|
||
[ConditionalFact(nameof(IsLdapConfigurationExist))] | ||
public void TestOU() // adding and removing organization unit | ||
{ | ||
using (DirectoryEntry de = new DirectoryEntry(LdapConfiguration.Configuration.LdapPath, | ||
LdapConfiguration.Configuration.UserName, | ||
LdapConfiguration.Configuration.Password, | ||
LdapConfiguration.Configuration.AuthenticationTypes)) | ||
{ | ||
string ouName = "NetCoreDevs"; | ||
|
||
// ensure cleanup before doing the creation. | ||
DeleteOU(de, ouName); | ||
|
||
CreateOU(de, ouName, ".Net Core Developers Unit"); | ||
try | ||
{ | ||
SearchOUByName(de, ouName); | ||
} | ||
finally | ||
{ | ||
// Clean up the added ou | ||
DeleteOU(de, ouName); | ||
} | ||
} | ||
} | ||
|
||
[ConditionalFact(nameof(IsLdapConfigurationExist))] | ||
public void TestOrganizationalRole() // adding and removing users to/from the ou | ||
{ | ||
using (DirectoryEntry de = new DirectoryEntry(LdapConfiguration.Configuration.LdapPath, | ||
LdapConfiguration.Configuration.UserName, | ||
LdapConfiguration.Configuration.Password, | ||
LdapConfiguration.Configuration.AuthenticationTypes)) | ||
{ | ||
DeleteOU(de, "CoreFxRootOU"); | ||
|
||
using (DirectoryEntry rootOU = CreateOU(de, "CoreFxRootOU", "CoreFx Root OU")) | ||
{ | ||
try | ||
{ | ||
DirectoryEntry child1OU = CreateOU(rootOU, "CoreFxChild1OU", "CoreFx Child OU 1"); | ||
DirectoryEntry child2OU = CreateOU(rootOU, "CoreFxChild2OU", "CoreFx Child OU 2"); | ||
|
||
CreateOrganizationalRole(child1OU, "user.ou1.1", "User 1 is in CoreFx ou 1", "1 111 111 11111"); | ||
CreateOrganizationalRole(child1OU, "user.ou1.2", "User 2 is in CoreFx ou 1", "1 222 222 22222"); | ||
|
||
CreateOrganizationalRole(child2OU, "user.ou2.1", "User 1 is in CoreFx ou 2", "1 333 333 3333"); | ||
CreateOrganizationalRole(child2OU, "user.ou2.2", "User 2 is in CoreFx ou 2", "1 333 333 3333"); | ||
|
||
// now let's search for the added data: | ||
SearchOUByName(rootOU, "CoreFxChild1OU"); | ||
SearchOUByName(rootOU, "CoreFxChild2OU"); | ||
|
||
SearchOrganizationalRole(child1OU, "user.ou1.1"); | ||
SearchOrganizationalRole(child1OU, "user.ou1.2"); | ||
|
||
SearchOrganizationalRole(child2OU, "user.ou2.1"); | ||
SearchOrganizationalRole(child2OU, "user.ou2.2"); | ||
} | ||
finally | ||
{ | ||
// rootOU.DeleteTree(); doesn't work as getting "A protocol error occurred. (Exception from HRESULT: 0x80072021)" | ||
DeleteOU(de, "CoreFxRootOU"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private DirectoryEntry CreateOU(DirectoryEntry de, string ou, string description) | ||
{ | ||
DirectoryEntry ouCoreDevs = de.Children.Add($"ou={ou}","Class"); | ||
ouCoreDevs.Properties["objectClass"].Value = "organizationalUnit"; // has to be organizationalUnit | ||
ouCoreDevs.Properties["description"].Value = description; | ||
ouCoreDevs.Properties["ou"].Value = ou; | ||
ouCoreDevs.CommitChanges(); | ||
return ouCoreDevs; | ||
} | ||
|
||
private DirectoryEntry CreateOrganizationalRole(DirectoryEntry ouEntry, string cn, string description, string phone) | ||
{ | ||
DirectoryEntry cnEntry = ouEntry.Children.Add($"cn={cn}","Class"); | ||
cnEntry.Properties["objectClass"].Value = "organizationalRole"; | ||
cnEntry.Properties["cn"].Value = cn; | ||
cnEntry.Properties["description"].Value = description; | ||
cnEntry.Properties["ou"].Value = ouEntry.Name; | ||
cnEntry.Properties["telephoneNumber"].Value = phone; | ||
cnEntry.CommitChanges(); | ||
return cnEntry; | ||
} | ||
|
||
private void DeleteOU(DirectoryEntry parentDe, string ou) | ||
{ | ||
try | ||
{ | ||
// We didn't use DirectoryEntry.DeleteTree as it fails on OpenDJ with "A protocol error occurred. (Exception from HRESULT: 0x80072021)" | ||
// Also on AD servers DirectoryEntry.Children.Remove(de) will fail if the de is not a leaf entry. so we had to do it recursively. | ||
DirectoryEntry de = parentDe.Children.Find($"ou={ou}"); | ||
DeleteDirectoryEntry(parentDe, de); | ||
} | ||
catch | ||
{ | ||
// ignore the error if the test failed early and couldn't create the OU we are trying to delete | ||
} | ||
} | ||
|
||
private void DeleteDirectoryEntry(DirectoryEntry parent, DirectoryEntry de) | ||
{ | ||
foreach (DirectoryEntry child in de.Children) | ||
{ | ||
DeleteDirectoryEntry(de, child); | ||
} | ||
|
||
parent.Children.Remove(de); | ||
parent.CommitChanges(); | ||
} | ||
|
||
private void SearchOUByName(DirectoryEntry de, string ouName) | ||
{ | ||
using (DirectorySearcher ds = new DirectorySearcher(de)) | ||
{ | ||
ds.ClientTimeout = new TimeSpan(0, 2, 0); | ||
ds.Filter = $"(&(objectClass=organizationalUnit)(ou={ouName}))"; | ||
ds.PropertiesToLoad.Add("ou"); | ||
SearchResult sr = ds.FindOne(); | ||
Assert.Equal(ouName, sr.Properties["ou"][0]); | ||
} | ||
} | ||
|
||
private void SearchOrganizationalRole(DirectoryEntry de, string cnName) | ||
{ | ||
using (DirectorySearcher ds = new DirectorySearcher(de)) | ||
{ | ||
ds.Filter = $"(&(objectClass=organizationalRole)(cn={cnName}))"; | ||
ds.PropertiesToLoad.Add("cn"); | ||
SearchResult sr = ds.FindOne(); | ||
Assert.Equal(cnName, sr.Properties["cn"][0]); | ||
} | ||
} | ||
} | ||
} |