Skip to content

Commit

Permalink
First DirectoryServcies tests which runs against real server (dotnet/…
Browse files Browse the repository at this point in the history
…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
tarekgh authored Sep 29, 2017
1 parent 17d1e6d commit fe5e86f
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 5 deletions.
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;
}
}
}
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>
-->
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<Compile Include="System\DirectoryServices\ActiveDirectorySecurityTests.cs" />
<Compile Include="System\DirectoryServices\ActiveDirectory\DomainControllerTests.cs" />
<Compile Include="System\DirectoryServices\DirectoryEntryTests.cs" />
<Compile Include="System\DirectoryServices\DirectoryServicesTests.cs" />
<Compile Include="System\DirectoryServices\DirectorySynchronizationTests.cs" />
<Compile Include="System\DirectoryServices\DirectoryVirtualListViewContextTests.cs" />
<Compile Include="System\DirectoryServices\DirectoryVirtualListViewTests.cs" />
Expand All @@ -18,6 +19,15 @@
<Compile Include="System\DirectoryServices\ActiveDirectory\ActiveDirectoryInterSiteTransportTests.cs" />
<Compile Include="System\DirectoryServices\ActiveDirectory\DirectoryContextTests.cs" />
<Compile Include="System\DirectoryServices\ActiveDirectory\ForestTests.cs" />

<Compile Include="$(CommonTestPath)\System\DirectoryServices\LdapConfiguration.cs">
<Link>Common\DirectoryServices\LdapConfiguration.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="LDAP.Configuration.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ public void FindByTransportType_ForestNoDomainAssociatedWithName_ThrowsActiveDir
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Not approved COM object for app")]
public void FindByTransportType_ForestNoDomainAssociatedWithName_ThrowsActiveDirectoryOperationException_NoUap()
{
var context = new DirectoryContext(DirectoryContextType.Forest, "\0");
AssertExtensions.Throws<ArgumentException>("context", () => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc));
// Domain joined machines will not throw on the ActiveDirectoryInterSiteTransport.FindByTransportType call.
if (Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase))
{
var context = new DirectoryContext(DirectoryContextType.Forest, "\0");
AssertExtensions.Throws<ArgumentException>("context", () => ActiveDirectoryInterSiteTransport.FindByTransportType(context, ActiveDirectoryTransportType.Rpc));
}
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,13 @@ public void FindOne_InvalidName_ThrowsException(string name, Type exceptionType)
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap, "Not approved COM object for app")]
public void FindAll_NoSuchName_ReturnsEmpty()
{
var context = new DirectoryContext(DirectoryContextType.Domain, "\0");
Assert.Empty(DomainController.FindAll(context));
Assert.Empty(DomainController.FindAll(context, "siteName"));
// Domain joined machines can have entries in the DomainController.
if (Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase))
{
var context = new DirectoryContext(DirectoryContextType.Domain, "\0");
Assert.Empty(DomainController.FindAll(context));
Assert.Empty(DomainController.FindAll(context, "siteName"));
}
}

[Fact]
Expand Down
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]);
}
}
}
}

0 comments on commit fe5e86f

Please sign in to comment.