Monday, May 6, 2013

Adding Objects to Non-Microsoft LDAP Directories in .Net and PowerShell

In this series on working with non-Microsoft directories in PowerShell and .Net


A previous post of mine that covered LDAP searches in non-Microsoft Active Directory LDAP directories (ex. OpenLDAP, Oracle Internet Directory, Novell eDirectory, etc.) gained a fair amount of popularity and I received requests to follow up on it to provide instructions on performing other LDAP functions using the .Net Framework and PowerShell.

Continuing from my last post, I went ahead and set up an OpenLDAP server for demonstration purposes utilizing the core, cosine, and inetorgperson schemas. I have prepopulated it with the following objects:

dn: dc=mikesblog,dc=lan
objectclass: dcObject
objectclass: organization
o: Mikes Technology Blog
dc: mikesblog

dn: cn=Manager,dc=mikesblog,dc=lan
objectclass: organizationalRole
cn: Manager

dn: ou=testou1,dc=mikesblog,dc=lan
objectclass: organizationalUnit
objectclass: top
ou: testou1
description: My first test OU

dn: cn=mike,ou=testou1,dc=mikesblog,dc=lan
objectClass: person
objectclass: inetorgperson
givenName: Mike
sn: Burr
uid: mike

dn: ou=testou2,dc=mikesblog,dc=lan
objectclass: organizationalUnit
objectclass: top
ou: testou2
description: My second test OU

In this example, we will add another inetorgperson object to the testou1 OU. To accomplish this, we will use the .Net framework classes in the System.DirectoryServices.Protocols namespace.  The LdapConnection class gives our application connectivity to the LDAP server where we can provide an AddRequest object and receive an AddReply.

The sample code below is designed to demonstrate how to create an LDAP entry, in practice this might be designed to use the command, content enricher, and decorator patterns instead of the hardcoded transaction script below.

Let's get to it... The sample C#.Net code:



// Connects to myopenldap.mikesblog.lan
// without SSL on the standard port
LdapConnection c = new LdapConnection("myopenldap.mikesblog.lan:389");

//Set session options
c.SessionOptions.SecureSocketLayer = false;
c.SessionOptions.ProtocolVersion = 3;

// Pick Authentication type:
// Anonymous, Basic, Digest, DPA (Distributed Password Authentication),
// External, Kerberos, Msn, Negotiate, Ntlm, Sicily
c.AuthType = AuthType.Basic;


// Gets username and password. There are better
// ways to do this more securely...
// but that's not the topic of this post.
Console.Write("Enter Username: ");
string username = Console.ReadLine();

Console.WriteLine();

Console.Write("Enter Password: ");
string password = Console.ReadLine();

// Bind with the network credentials. Depending on the type of server,
// the username will take different forms. Authentication type is controlled
// above with the AuthType
c.Bind(new System.Net.NetworkCredential(username, password));

// We are going to create this object (LDIF Below)
/*

dn: cn=testuser,ou=testou1,dc=mikesblog,dc=lan
objectclass: person
objectclass: inetorgperson
givenName: Test
sn: User
uid: testuser

*/


AddRequest r = new AddRequest();
r.DistinguishedName = "cn=testuser,ou=testou1,dc=mikesblog,dc=lan";

//Use the Attributes dictionary and the
//DirectoryAttribute class to set the desired attributes

r.Attributes.Add(
    new DirectoryAttribute(
        "objectclass",
        new object[] {"person","inetorgperson"}
    )
);

r.Attributes.Add(
    new DirectoryAttribute("givenName", "Test")
);

r.Attributes.Add(
    new DirectoryAttribute("sn", "user")
);

r.Attributes.Add(
    new DirectoryAttribute("uid", "testuser")
);



//Actually process the request through the server
AddResponse re = (AddResponse)c.SendRequest(r);

if (re.ResultCode != ResultCode.Success)
{
    Console.WriteLine("Failed!");
    Console.WriteLine("ResultCode: {0}", re.ResultCode);
    Console.WriteLine("Message: {0}", re.ErrorMessage);

}

Console.Read();


Not to disappoint, below is the PowerShell port of the above code.


#Mike Burr
#Script Connects to and Creates Objects in OpenLDAP directory

#Load the assemblies
[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")


#Connects to myopenldap.mikesblog.lan on the standard port
$c = New-Object System.DirectoryServices.Protocols.LdapConnection `
     "myopenldap.mikesblog.lan:389"
          
#Set session options
$c.SessionOptions.SecureSocketLayer = $false;
$c.SessionOptions.ProtocolVersion = 3

# Pick Authentication type:
# Anonymous, Basic, Digest, DPA (Distributed Password Authentication),
# External, Kerberos, Msn, Negotiate, Ntlm, Sicily
$c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic

# Gets username and password.
$user = Read-Host -Prompt "Username"
$pass = Read-Host -AsSecureString "Password"

$credentials = new-object "System.Net.NetworkCredential" -ArgumentList $user,$pass

# Bind with the network credentials. Depending on the type of server,
# the username will take different forms. Authentication type is controlled
# above with the AuthType
$c.Bind($credentials);

# We are going to create this object (LDIF Below)
#

# dn: cn=testuser,ou=testou1,dc=mikesblog,dc=lan
# objectclass: person
# objectclass: inetorgperson
# givenName: Test
# sn: User
# uid: testuser

$r = (new-object "System.DirectoryServices.Protocols.AddRequest")

$r.DistinguishedName = "cn=testuser,ou=testou1,dc=mikesblog,dc=lan";

#Use the Attributes dictionary and the
#DirectoryAttribute class to set the desired attributes

$r.Attributes.Add( `
    (new-object "System.DirectoryServices.Protocols.DirectoryAttribute" `
       -ArgumentList "objectclass",@("person","inetorgperson") `
    ) `
)

$r.Attributes.Add( `
    (new-object "System.DirectoryServices.Protocols.DirectoryAttribute" `
    -ArgumentList "givenName","Test") `
)

$r.Attributes.Add( `
    (new-object "System.DirectoryServices.Protocols.DirectoryAttribute" `
    -ArgumentList "sn","User") `
)

$r.Attributes.Add( `
    (new-object "System.DirectoryServices.Protocols.DirectoryAttribute" `
    -ArgumentList "uid","testuser") `
)



#Actually process the request through the server
$re = $c.SendRequest($r);

if ($re.ResultCode -ne [System.directoryServices.Protocols.ResultCode]::Success)
{
    write-host "Failed!"
    write-host ("ResultCode: " + $re.ResultCode)
    write-host ("Message: " + $re.ErrorMessage)
}




1 comment: