Using DirectoryServices for Novell eDirectory

Thursday, 8 July 2010 00:08 by RanjanBanerji

I recently had to work with creating and editing accounts in Novell Directory Services (NDS) or eDirectory.  The LDAP application comes with C and Java SDK and lately even boasts a .Net SDK.  Its the .Net SDK that I set out to use.  You can find detailed documentation on how to use it here:  http://www.novell.com/coolsolutions/feature/11204.html.  Also here:  http://www.novell.com/communities/node/8772/setting-novell-ldap-libraries-c.

Both the links above require that you download the Novell LDAP SDK in C# assembly from here ftp://forgeftp.novell.com/ldapcsharp/ldapcsharp/CsharpLDAP-v2.1.11.  The problem with using the Novell SDK for eDirectory is that:

  1. If you are developing a Windows based application you need to go get the Mono.Security.dll.  Not a big deal but an additional step.
  2. You then need to configure your server/s to have a key store.  You will have to download other mono utilities etc.  Its all in the documentation.  Not difficult but additional steps.
  3. The link for downloading the  Novell LDAP SDK in C# (ftp://forgeftp.novell.com/ldapcsharp/ldapcsharp/CsharpLDAP-v2.1.11) has been down for months.  I sent Novell an email, a month later I got a response the problem is being fixed.  as of this writing the download site is still unreachable.

Clearly I needed a better approach.  I searched the web and found a few roundabout approaches and then finally decided to use DirectoryServices as packaged with the .Net framework.  At first I was getting no luck.  I was getting extremely meaningful (sarcasm) COM error messages that perhaps even Microsoft cannot decipher.  So I decided to look into the documentation for the Novell eDiretory web services, C SDK and Java SDK.  The three put together gave some valuable insights.  In retrospect if I had a tool to browse their schema things could have been easier.

Anyway, since my task was relatively easy what I am about to show does not cover all that is eDirectory and its SDK.  I simply needed to add, edit and delete users.  The code below is merely a rough example.  I am sure it can be written much more elegantly.  As I often tell developers, you are extremely well paid.  So put in some effort and make it elegant :-)

The main issue with using the DirectoryServices classes for eDirectory is to understand the subtle differences in syntax.

Also, if you are using SSL for your communication to eDirectory you will have to issue a certificate from eDiretory and install it on the server.  In this case this is much easier than the process required for the Novell dll that requires various mono dlls and tools and steps.  Just double click on the certificate file and Windows will install it.

Here is the sample code to add, edit, delete a user.

using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Net;
using System.Linq;
using System.Text;


namespace eDirectory {
    class Program {
        static void Main( string[] args ) {
            //Connect using SSL port 636 default
            //In the function below I believe for Active Directory you just put the username as in "bob".  
            //For eDirectory you need to put the proper dn as shown below
            DirectoryEntry de = new DirectoryEntry( "LDAP://YourServerName.YourCompanyName.com:636/ou=YourOuName,o=YourCompanyName", 
                "cn=ADMINUser,o=YourCompanyName", "AdminPASSWORD", AuthenticationTypes.ServerBind );
            //Here again in AD I believe you say "user"
            DirectorySearcher ds = new DirectorySearcher( de, "(objectClass=user)" );

            //The code below is just a test to see if I can properties for all users.  
            //The code does not work since not all properties are strings.  But I can print 
            //all the property names
            var test = ds.FindAll();
            foreach( SearchResult result in test ) {
                foreach( string propName in result.Properties.PropertyNames ) {
                    Console.Write( propName + "\t" );
                    //foreach( string val in result.Properties[ propName ] ) {
                    //    Console.Write( val + "\t" );
                    //}
                    Console.WriteLine( " " );
                }
            }

            string username = "newUserName";
            string givenname = "SomeGivenName";
            string sn = "Five";
            string password = "1234!5";
            string newPassword = "123!15";
            string newUserName = "NewUserNameforUser";

            CreateUser( de, username, givenname, sn, password );
            //ChangePassword( de, username, newPassword );
            //DeleteUser( de, username );
        }

        /// <summary>
        /// Deletes a user
        /// </summary>
        /// <param name="de"></param>
        /// <param name="username">User to delete</param>
        private static void DeleteUser( DirectoryEntry de, string username ) {
            //Note syntax for filter
            DirectorySearcher ds = new DirectorySearcher( de, "(&(objectclass=User)" + "(cn=" + username + "))" );
            var test = ds.FindOne();
            if( test != null ) {
                //Note proper dn is required 
                DirectoryEntry userDE = new DirectoryEntry( test.Path, "cn=ADMINUser,o=YourCompanyName", 
                    "AdminPASSWORD", AuthenticationTypes.ServerBind );
                userDE.DeleteTree();
                de.CommitChanges();
            }
        }

        /// <summary>
        /// Edits a user.  Changes password.  Notice difference in syntax compared to AD.  AD uses an Invoke command.
        /// </summary>
        /// <param name="de"></param>
        /// <param name="username"></param>
        /// <param name="newPassword"></param>
        private static void ChangePassword( DirectoryEntry de, string username, string newPassword ) {
            DirectorySearcher ds = new DirectorySearcher( de, "(&(objectclass=User)" + "(cn=" + username + "))" );
            var test = ds.FindOne();

            if( test != null ) {
                //Note proper dn is required 
                DirectoryEntry userDE = new DirectoryEntry( test.Path, "cn=ADMINUser,o=YourCompanyName", 
                    "AdminPASSWORD", AuthenticationTypes.ServerBind );
                userDE.Properties[ "userPassword" ].Add( newPassword );
                userDE.CommitChanges();
            }
        }

        /// <summary>
        /// Creates a user
        /// </summary>
        /// <param name="de"></param>
        /// <param name="username"></param>
        /// <param name="givenname"></param>
        /// <param name="sn"></param>
        private static void CreateUser( DirectoryEntry de, string username, string givenname, string sn, string newPassword ) {
            DirectoryEntry newDE = de.Children.Add( "cn=" + username, de.SchemaClassName );
            newDE.Properties[ "objectclass" ].Add( "top" );
            newDE.Properties[ "objectclass" ].Add( "person" );
            newDE.Properties[ "objectclass" ].Add( "organizationalPerson" );
            newDE.Properties[ "objectclass" ].Add( "inetOrgPerson" );
            newDE.Properties[ "objectclass" ].Add( "ndsLoginProperties" );
            newDE.Properties[ "givenname" ].Add( givenname );
            newDE.Properties[ "sn" ].Add( sn );
            newDE.Properties[ "title" ].Add( "BLAH" );
            newDE.Properties[ "userPassword" ].Add( newPassword );

            newDE.CommitChanges();
        }
    }
}

Comments

July 16. 2010 12:20

Amol

Since I found this post trying to figure out how to download the Novell c# dll, the FTP server mentioned above is still down.  But they have an alternate at http://chorgan.provo.novell.com/ldapcsharp/

Amol

July 17. 2010 19:58

Ranjan Banerji

Thanks Amol.

Ranjan Banerji

March 7. 2011 11:09

Sheldon

The libraries moved once again.

ftp://sdk.provo.novell.com/ndk/ldapcsharp/ldapcsharp/CsharpLDAP-v2.1.11

Sheldon

Sheldon

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading