Introduction
The following document provides an overview of the
ClLdap
and related classes
that implements "portable" code that support LDAP to manage directory services
from an application.
ClLdap
is a high level class and hides most
details of managing a connection and directories. The current version also
provides MS-SQL external procedures to manage the
ClLdap
class and LDAP
request queueing using MS-Message Queue based on LDIF wrapped by a XML
document (read on for details). To handle queued transactions I have added a
service that will run in NT/W2k that fetches the transactions from the queue
and sudmit them to the Directory Services server using LDAP (protocol).
Why take a look at this work? You can do it your self, but I have spent a lot
of time working and experimenting and coding for LDAP and trying to maintain interoperable
(W2K to UNIX and vs) code that implements it. This experience
I am trying to give to you may be of value so take a peek. The code samples
in other references provide you with samples of calling LDAP API functions,
Here I have tried to implement a collection of classes that from my point of
view are a lot more simpler to use. For example, with the ClLdap
and ClLdapUser
classes you can manage users as simple as:
int main(int argc, char* argv[])
{
ClLdap myLdap ;
if (myLdap.Connect()) {
if (myLdap.AuthenticateUser()) {
ClLdapUser myuser(&myLdap,"CN=Users,OU=MyOrganization") ;
if (!myuser.Exists("Maria"))
myuser.Add("Maria","batata") ;
if (!luser.Exists("Carmina"))
myuser.Add("Carmina","bacalao") ;
myuser.SetPassword("Maria","malanga") ;
char rVal[MINSTRLEN+1] ;
myuser.Find("Maria","cn",rVal,MINSTRLEN) ;
cout << "Maria's CN=" << rVal << endl ;
myuser.Authenticate("Maria","malanga") ;
myuser.Delete("Maria") ;
myuser.Delete("Carmina") ;
}
myLdap.Disconnect() ;
}
}
To learn about LDAP and Active Directory you should get acquainted with
the terminology (eg. LDAP, RDN, ...). Beside that, the above code does
a lot in very few lines. A nice thing about it is that you will be able to
compile the code for Windows 2K or UNIX and "it will work just as well".
This is not the place to look for an overview of Active Directory and/or LDAP
this is better covered in given
references. Still
I will highlight some issues that in my LDAP experimenting / learning
where confusing and required some info digin, therefore I am taking some time
to explain
the things that you should know. If you are
interested in LDAP API programming you can jump into the code it self.
I will provide a "Code Package" with all the code for
your enjoyment. Details about this package is what follows.
This is my list of things that you as a minimum should understand to be able
to manage the directory services:
Know Your Schema
The first thing you should understand is the schema that your directory is
using. The directory has a hierrachical structure as a tree with nodes and
leafs. A node and a leaf do have a collection of attributes and those
values. The schema has the "metadata" of those attributes and tree objects.
In the schema you will find that a node (or a directory object) had to had
of some kind (lets say a "User") needs to have a minimum set of attributes
to be able to be added and manage. For each attribute defined there are a
collection of constraints that most be followed. My advice is that you sit
down and spend some time browsing the directory and look at objects,
attributes and their values.
A nice tool to do so is the "adsvw.exe" application. When you run it the
first thing it will ask you is how you whant to look at the directory.
For quick browsing select the "ObjectViewer" option by pressing the "OK"
button. Next it will display a dialog box so you type what you will like
to view. Here type "ADs:" and unselect the "Use OpenObject" box. By
unselecting open object you will be binded (logged in) without entering
any id or password. The "ADs:" in turn will possition you at the top (root)
of the directory. If all goes well you will be given a "tree viewer" kind of
dialog are where you can browse the tree and at the right a view of the
object particulars that you are browsing (or possition at). You should find
the "adsvw.exe" application in the SDK directory.
Know How To Refer To Entries
This is the next thing that you should master. In the general literature you
will find different terms related to an entry position in the tree. The path
of an entry in the directory is also known as the "Distinguished Name" or
"DN" for short. A partial path is refer as a "Relative Distringushed Name"
or "RDN" for short. You will here a lot about the "Naming Context". When you
connect to a server this server does have a base or a its root, this must
probably not the top of the organization tree but the root of the local
hierrarchy. We will refer to this as the "Naming Context" where we will
use as our root. For example, an organization that has a corporate office
in Puerto Rico may have a domain controller with a "Naming Context" as
"corporate.pr.my-organization.com".
If you are lots, just have in mind that the LDAP server that you are trying
to connect does have a name (a path) in the directory that you use to refer
to it. To now about this name go to the "Control Panel" then select the
"System" component and click on the "Network Identification" tab. Here you
will see the full name of the host, for example
"myhost.corporate.pr.my-organization.com". In this example "myhost" is the
name of the computer and "corporate.pr.my-organization.com" the base root
of your site or what we will call the "Default Naming-Context".
Now that you master how to refer where you are, now you need to know how to
refer to objects in the tree. Since this is a tree structure you are
probably thinking that is similar to you usual file path names. Yes it is
very similar, still you need to get use to tagging each entry item with its
attribute name. LDAP entries are refered using the attribute names and their
values for example in our sample for the "corporate.pr.my-organization.com"
the LDAP real name is:
"DC=corporate,DC=pr,DC=my-organization,DC=com"
Using
Microsoft schema for Active Directory the users will be located at:
"CN=Users, DC=corporate, DC=pr, DC=my-organization, DC=com"
And a particular
user (lets say "Maria del Mar") at:
"CN=Maria del Mar,CN=Users, DC=corporate, DC=pr, DC=my-organization, DC=com"
To refer to objects from ADSI point of view you will access or refer to a
directory entry by appending "LDAP://" infront of your DN, for example the
Maria's directory will be:
"LDAP://CN=Maria del Mar,CN=Users,DC=corporate,DC=pr,DC=my-organization,DC=com"
In summary, every object in the directory has a naming attribute. This
attribute serves as the objects's name withing its container.
Some of them are:
ATTRIBUTE | Meaning of Relative Distinguished Namimg Attributes |
cn | (common name) Attribute for user objects. |
dc | (domain component) Naming attributes for domains. |
o | (organization) |
l | (locality) |
ou | (organizational unit) |
st | (state or province) |
c | (country) |
street | (steet) |
uid | (user id) |
sn | (organization) |
The "Distinguished Name" (or DN) of an object is the RDN of the
object concatenated with the DN of its container.
The fully qualified name of an object in an LDAP directory
(its distringuished name) is the concatenation of its relative
distringuished name and the distinguished name of its container.
Know That LDAP Uses Unicode
In fact it uses UTF-8 format [8-bits chars] to represent chars
of a string. Here the highest order bit is checked and if it is
0 (that is the same as using ASCII codes less than 128), then
the characters are assumed to be represented in one byte. Else
if the highest level bit is one, the char spans at two bytes.
When using Microsoft LDAP API you will not need to be aware of
it using UTF-8 since you will use ASCII strings and they later
convert them in the UTF-8 format.
Know About Managing Password/s
You can't change a user password using LDAP. Windows Active
Directory stores the password in the "unicodePwd" attribute
of a user object as an "octectString". If you read the MSDN
as of April/2k the definition for the "unicodePwd" is:
"The unicodePwd property is the password for the user. For
setting the password of the user, you should use the
IADsUser::ChangePassword method (if your script or application
is allowing the user to change his/her own password) or
IADsUser::SetPassword method (if your script or application is
allowing an administrator to reset a password). The password of
the user in Windows NT (NT) one-way format (OWF). Windows® 2000
uses the NT OWF. This property is used only by operating system.
Note that you cannot derive the clear password back from the OWF
form of the password...".
I implemented a change password
function that will not port to UNIX since it uses ADSI stuff.
Therefore don't try to call it on UNIX boxes...
Lets clarify some issues here. You can't change the user password
represented in "unicodePwd" using LDAP as implemented in W2k (if you do
have some info on how to do so, please tell me I have not find a way to
do so), still, you can authenticate any user as a system user from any other
box (NT, W2k or UNIX) by trying to bind it (calling ldap_bind or
ldap_simple_bind or related calls [see ClLdap::Authenticateuser method
in "ClLdap.cpp" that does exactly that]). If you need to manage and change
the password of a system user you where the LDAP server is MS-AD you will
doit using W2k Administrative tools or invoking ADSI the related methods
to do so (again in a W2k environment).
Still if what you need is to manage none system users (users that will
never logon into the system or need to do so) you may manage their password
using the "userPassword" available in the "Users" object. This is an
"octect string", still you can set it and request it as you like. The
provided "ClLdapUser" do just that.
Know About What Can Be Done With The LDAP API's
It is very simple and intuitive. The operations that can be done are:
- Connect to an LDAP server. (ldap_init)
- Authenticate a user. (ldap_bind)
- Add entries. (ldap_add)
- Modify entries. (ldap_modify)
- Remove entries. (ldap_delete)
- Search for entries. (ldap_search)
As you can see, there is not much to it.
The given "ldap_" functions are the base ones. They come in two flavors
"synchronous" and "asynchronous", for example (ldap_init / ldap_init_s).
In this first version of the ClLdap classess I am not using the "async"
flavor, still I have implemented an tested a method called
"ClLdap::WaitForOneResult(int iMessid)" that will wait for one result when
calling the "async" related call (the ones that don't end with "_s").
Probably the most controvertial of all operations is the searching of info.
Don't think that you can look for an attribute and have a value. What you
must likely will get is a result set. In plain LDAP API use, you will need to
be continously requesting for next item in the result set to get the
attributes of an object and for each attribute the set of values. I have
simplify this operation by hiding all details of moving from attribute to
attribute and while fetching values, from attribute to attribute (see
Searching For An Entry sample code).
Know About Importing Data To A Directory
To do so you will use a standard format LDIF (LDAP Data Interchange Format).
I have implemented and tested a bit of it an LDIF reader with the LDIFEntries
class. If you call the "ClLdap::LoadLDIF(char *fname)" method it will try
to read an LDIF document and do as needed to add the entries. It should work
still I have not tested it in detail.
The ClLdap and related classess will give you a headstart to implement more
complex LDAP applications. This first implementation I concentrated using
the synchronious LDAP calls. Still I have tested some support that is already
there for async invocation and works great. In summary the implemented
classess are:
CLASS | DESCRIPTION |
ClLdap | Provides you to connect, authenticate, set defaults,
find entries, dump a tree, add and delete entries. |
LDAPEntry | This class supports searching / traversing the directory tree. |
LDIFEntries | Will help you in building entries and support the LDIF (LDAP
Data Interchange Format) to manage and populate the directory. |
ClLdapUser | Using the previous classess this one is oriented to manage basic
user management such as adding, updating and removing entries. |
Sample code using the classes will follow.
Connecting To The Server
Connecting to the server and getting an LDAP session is the first thing you
will do. The next thing to do is to authenticate your self, else the session
is worthless. Here (see below) I pass "NULL" to the "Connect" method to
connect to the default directory server. If no argument is given as the
example at the begining of the document, it will connect as if "NULL" where
passed.
int main(int argc, char* argv[])
{ ClLdap myLdap ;
if (myLdap.Connect(NULL)) {
if (myLdap.AuthenticateUser(NULL,NULL,AUTH_LOGGED_USER)) {
}
myLdap.Disconnect() ;
}
}
For authentication I provide some possibilities:
METHOD | DESCRIPTION |
AUTH_ANNONYMOUS | This method will ignore the user and password and will try
to connect asynchrously as nobody. |
AUTH_CLEAR_TEXT | Provided a user "Common Name" and a password, this call will
try to connect the user asynchrously. |
AUTH_LOGGED_USER | This authentication method will try to negotiate the
authentication and as implemented will try to log with current
user credentials. |
After you have connected and you are authenticated you can search, add,
modify and delete entries.
Adding, Modifying and Removing Entries
With the LDIFEntries and LDAPEntry classes adding, modifying and removing
entris is very simple (as implemented in the ClLdapUser class) as shown
below.
bool ClLdapUser::Add(char *pUserId,char *pPassword)
{ bool isok ;
if (pUserId && pUsersDN && pLdap) {
char str[MINSTRLEN] ;
sprintf(str,"CN=%s,%s",pUserId,pUsersDN) ;
LDIFEntries entries(pLdap->psLdap,str) ;
entries.AppendToAdd("objectClass","user") ;
entries.AppendToAdd("cn",pUserId) ;
entries.AppendToAdd("sAMAccountName",pUserId) ;
if (pPassword)
entries.AppendToAdd("userPassword",pPassword) ;
isok = entries.Add() ;
}
else
isok = false ;
return(isok) ;
}
Let's do some explaining here. This method takes a user-id (eg. "Maria")
and a clear text password and set's the "userPassword" attribute to it.
this attribute is not the actual user password used by W2k to authenticate
a system user. It is an "octectString" attribute that your application have
access to do password management not related to NT system security. I do use
it for handling users that are not realy part of the system and that don't
have access to it. Such as those of applications accessed through the Web.
If you take a look in the MSDN and search for "userPassword" you will find
that "Site Server" does allow its use to handle subcriptors.
The "pUsersDN" used here is set when you create an instance of the
"ClLdapUser" class and represents the RDN of the users path. The "pLdap"
variable is also set at creation time and represents a pointer to an
instantiated "ClLdap" object.
bool ClLdapUser::SetPassword(char *pUserId,char *pNewPassword)
{ bool isok ;
if (pUserId && pUsersDN && pLdap && pNewPassword) {
char str[MINSTRLEN] ;
sprintf(str,"CN=%s,%s",pUserId,pUsersDN) ;
LDIFEntries entries(pLdap->psLdap,str) ;
if (pNewPassword)
entries.AppendToUpdate("userPassword",pNewPassword) ;
isok = entries.Update() ;
}
else
isok = false ;
return(isok) ;
}
As
documented earlier the "ClLdapUser" class uses the
"userPassword" attribute to store and handle the user password. For an
example on how to fetch this password take a look at the "ClLdapUser::Authenticate(...)"
method.
bool ClLdapUser::Delete(char *pUserId)
{ bool isok ;
if (pUserId && pUsersDN && pLdap) {
char str[MINSTRLEN] ;
sprintf(str,"CN=%s,%s",pUserId,pUsersDN) ;
isok = pLdap->DeleteEntry(str) ;
}
else
isok = false ;
return(isok) ;
}
If you take a look into the "DeleteEntry" function you will see that it uses
the "ldap_delete_s" call to do so.
Searching For An Entry
To search for an entry I implement the "LDAPEntry" class. After setting
the base DN of your choice, the scope (BASE, ONELEVEL or SUBSTREE), and
the object class (or what ever you set so) and what attribute you are looking
for you will get a result set for your query. If you enter "NULL" for the
target object or the attribute it will fetch everything. By setting the
object class to "*" (eg. "objectclass=*") you will fetch all types of
objects.
bool ClLdapUser::Find(char *pUserId,char *pAttr,char *pValue,int iValLen)
{ bool isok = false ;
if (pUserId && pUsersDN && pLdap) {
char str[MINSTRLEN] ;
sprintf(str,"CN=%s",pUserId) ;
LDAPEntry ent ;
ent.psLdap = pLdap->psLdap ;
ent.pNamingContext = pUsersDN ;
ent.Find(str,LDAP_SCOPE_BASE,"objectclass=user",pAttr) ;
while (!ent.Eof) {
if (!strcmp(pAttr,ent.pEntryName)) {
int iLen = strlen(ent.pEntryValue) ;
if (iLen > iValLen) iLen = iValLen ;
memcpy(pValue,ent.pEntryValue,iLen) ;
pValue[iLen] = NULL ;
isok = true ;
break ;
}
ent.Next() ;
}
ent.Free() ;
}
return(isok) ;
}
I am not trying here to make you an LDAP searching guru, to get acquainted
with advance stuff consult provided
references, this
is a topic well discussed on any of those.
I have added support for using some of the ClLdap functionality by preparing
the 'clldaplib.dll' (dynamic link library). To use it just drop the 'dll'
into the ".../MSSQL/Binn" directory and execute the 'sp_addextendedproc'
MS-SQL store procedure to define the entry points for each function as
follows:
USE master
GO
EXEC sp_addextendedproc 'xp_ClLDAPConnect','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPDisconnect','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPSetOrganizationalUnitDN','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPAddUser','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPSetPassword','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPDeleteUser','clldaplib.dll' ;
EXEC sp_addextendedproc 'xp_ClLDAPSetQueueFormatName','clldaplib.dll' ;
As you know, you most be in the master database to be able to add / define
external procedures. Therefore we most move to the "master" database first.
Having defined 'dll' functions then you will be able to connect to an LDAP
server, add and remove users and change their passwords. If you prefer
you can queue the transactions (
see details in next
section). Here are some samples:
USE YourDatabaseName
GO
exec master..xp_ClLDAPConnect 'ldaphost','Administrator','admin-password' ;
exec master..xp_ClLDAPSetOrganizationalUnitDN 'ou=subcriptors,dc=mycompany,dc=com' ;
exec master..xp_ClLDAPAddUser 'maria','malanga' ;
exec master..xp_ClLDAPSetPassword 'maria','cocoloco' ;
exec master..xp_ClLDAPDeleteUser 'maria' ;
exec master..xp_ClLDAPDisconnect
exec master..xp_ClLDAPSetQueueFormatName
'PUBLIC=93517BE9-9EC7-4FC3-ABBC-E73E9DC3C0C7' ;
exec master..xp_ClLDAPAddUser 'carla','malanga' ;
exec master..xp_ClLDAPSetPassword 'carla','cocoloco' ;
exec master..xp_ClLDAPDeleteUser 'carla' ;
This "external procedures" are available to any database still you most
prefix the call with 'master..' to access them. As illustrated you most
define the DN where the users are located before requesting any add,delete or
set-password directory entries handling. If you prefer you can queue the
requests and in this case you can work offline. As you can guess this
functionality is only available in W2k/NT platform, the code will still
compile on UNIX boxes. You still can access such functionality using ODBC
from any other box independently of the OS.
Installing 'ClLdap' MS-SQL Support
I have put together a set of MS-SQL '*.sql' scripts that will help you
use the 'ClLdap' class while managing your database. To setup them you most
first install the 'dll' as explain above. Then run each script in the order
listed. To run the script open the 'MS-SQL Query Analyzer' connect to the
server by supplying a log id and password (eg. 'sa' and 'sa-password') and
then open each document and execute them by pressing 'F5'. The supplied
documents are:
STEP | DOCUMENT | DESCRIPTION |
1 | ClLdap0.sql | Assuming that the 'clldaplib.dll' is in place as already
explained, this script will move to the 'master' database and
define / setup the entry points to access the 'ClLdap'
functionality. |
2 | ClLdap1.sql | This creates a table that will be used to manage the accounts.
Included are a set of triggers that are invoke as accounts
are added, updated and deleted. Make sure that the supplied
triggers point to your 'Customers' table. Update the script
as needed to reflect account disabling and recconnection.
|
3 | ClLdap2.sql | While handling the ClLdap table you will need some triggers that
provide for the actual ClLdap invocation. This script contains
all triggers that call for adding, updating and removing
accoounts. Try to make adjustments on the 'ClLdap1' and not
here. |
N/A | ClLdapU.sql | The script will upload accounts from a given table to the
directory. Some assembly is require to configure the upload
to your specific needs. |
N/A | ClLdapX.sql | If you need to cleanup all 'ClLdap' stuff, use this script and
it will remove all traces of the LDAP support. |
After installing the above and configuring it for your particular database,
you need to test the installation by using your application and creating,
modifying and disabling/removing accounts.
Why queueing? Simple, because you don't need to be concern about being
connected or not, or if the server is available or not. Your transactions
will be processed when the server is available. This is probably the best
method if your LDAP server is on a remove site and/or network communication
are not that reliable, or just because you whant peace of mind. Microsoft
argues that the MSMQ technology will transfer the message in the order they
where queued and that only a single copy will be transfer. The message
queue has fancy mechanism to manage administration queues, receiving feed
back and other methods to help you be sure that transactions arrive and where
fetched from the queue. I will not take time to explain this kind of support
still the included code can be setup to support and take advantage of this
functionality.
I have been using the message queue since its first release v1.0, when it
was available through the NT options pack. In Windows 2000 you just go to
the control panel and select the add/remove programs component. Here select
"Add/Remove Windows Components" and look for Message Queue there. After the
queue is setup create a 'Public Queue' where we will insert the messages.
If you have not done this before, just look for the "Computer Management"
located in the "Start" -> "Programs" -> "Administrative Tools" menu. For
the message queue you will go to the "Services and Applications" on the left
window pane and click under "Message Queueing". On the "Public Queues" with
a left mouse button click you will select the "New" -> "Queue" option/s.
If you have NT you will install the message queue with the options pack and
when done select the "Message Queue Explorer" from the "Start" -> "Programs"
-> "NT Options Pack" menu. Here select your site and add a new queue. You
may need to change the default permitions from write only to read/write or
as needed.
Before we can use the ClLdap message queuing support we need to get the
queue format name. On any platfrom position the cursor in to of the queue
entry and click the left button and select "Properties". You are looking
for the "ID" of the queue (eg. "{93517BE9-9EC7-4FC3-ABBC-E73E9DC3C0C7}")
the number inside the curly brackets is what uniquely identify this queue.
To set the queue "Format Name" on the ClLdap class you will need to append
the "PUBLIC=" at the front, although this is not the only way to specify
this property, it does identify a public queue uniquely. As you probably
know you may also query for the format name by specifying the host and
symbolic queue name and translating the path to a format. I don't like
this method because will require that we are connected at least while
getting the format-name by querying the MS-Queue server that holds the
database. If we set the format by hand then we can connect without quering.
After all you will queue to a known queue always and should not be
creating or destroying it, therefore it is a static resource that should
always be available.
To use the queue just call "ClLdap::SetQueue(QueueFormatName)" and start
queuing requests. For example:
ClLdap myLdap ;
myLdap.SetQueue("PUBLIC=93517BE9-9EC7-4FC3-ABBC-E73E9DC3C0C7") ;
ClLdapUser user(&myLdap,"ou=cocos-host-computer,cn=MyCompany,cn=local") ;
user.Add("Maria","calabaza") ;
user.SetPassword("Maria","balcalao") ;
user.Delete("Maria") ;
After queue some request take a pick at the queue (you may need to
refresh its content). You will fine something like this (not related
to the above sample). This is a sample entry that I did from my
"cocos-host-computer.local" host where users are located at
"ou=subcriptors".
3C 6C 64 69 66 3E 3C 6C 6E <ldif><ln
3E 64 6E 3A 20 63 6E 3D 6A >dn: cn=j
75 61 6E 63 68 6F 2C 6F 75 uancho,ou
3D 73 75 62 63 72 69 70 74 =subcript
6F 72 73 2C 64 63 3D 62 75 ors,dc=co
69 6C 64 69 6E 67 76 63 73 cos-host-
6F 6C 75 74 69 6F 6E 73 2C computer,
64 63 3D 6C 6F 63 61 6C 3C dc=local<
2F 6C 6E 3E 3C 6C 6E 3E 63 /ln><ln>c
68 61 6E 67 65 74 79 70 65 hangetype
3A 20 41 64 64 3C 2F 6C 6E : Add</ln
3E 3C 6C 6E 3E 6F 62 6A 65 ><ln>obje
63 74 43 6C 61 73 73 3A 20 ctClass:
75 73 65 72 3C 2F 6C 6E 3E user</ln>
3C 6C 6E 3E 63 6E 3A 20 6A <ln>cn: j
75 61 6E 63 68 6F 3C 2F 6C uancho</l
6E 3E 3C 6C 6E 3E 73 41 4D n><ln>sAM
41 63 63 6F 75 6E 74 4E 61 AccountNa
6D 65 3A 20 6A 75 61 6E 63 me: juanc
68 6F 3C 2F 6C 6E 3E 3C 6C ho</ln><l
6E 3E 75 73 65 72 50 61 73 n>userPas
73 77 6F 72 64 3A 20 6D 61 sword: ma
6C 61 6E 67 61 3C 2F 6C 6E langa</ln
3E 3C 2F 6C 64 69 66 3E ></ldif>
I am also supplying an Window 2000 service that fetches the messages from the
queue and submit the requests to an LDAP server. As you can guess this
functionality is only available in W2k/NT platforms. The code will still
compile for UNIX boxes but will not be able to queue transactions. Using the
MS-SQL ClLdap support you can still access such functionality using ODBC
from any other box independently of the OS.
Setting Up The ClLDAP Queue Services
To do so, you will prepare an "*.ini" file that will describe to the service
how to connect to the directory server, the naming context and some other
runtime parameters to control retries, error log, and timeouts. The "ini"
file should include the following:
PARAMETER | DESCRIPTION |
formatname | Format Name of queue as explained above. |
queuetimeout | Timeout time in milliseconds that we will wait for a message
if you specify -1 it will wait for ever.
|
logid | Required login id to be able to connect to the Directory
server. |
password | Corresponding password for the previous login id. |
logpath | Directory path where to store log information. The full path
name is created (one by day) as %logpath% + "MMDDYYYY.log". |
ldaptimeout | Timeout time to wait for asyc requests. |
uselog | 'y' (in low case) to write to a log file. |
ldaphost | Host name (or it's IP) where the directory server is located. |
userdntemplate | Users DN where to locate the system user accounts. |
defaultnamingcontext | Naming context as above explained. |
connectretries | Number of connection retries before giving up. |
resendretries | Number of times to try to resend a request for processing
before giving up. |
sleeponretry | Sleep time before resending / reconnecting in milliseconds. |
A sample ini file follows (this are good defaults [for most of it]):
formatname = PUBLIC=93517BE9-9EC7-4FC3-ABBC-E73E9DC3C0C7
queuetimeout = -1
logid = Administrator
password = admin
logpath = c:/temp/
ldaptimeout = 1000
uselog = y
ldaphost = openk2
userdntemplate = "cn=%s,cn=Users,dc=openk,dc=com"
defaultnamingcontext = "dc=openk,dc=com"
connectretries = 3
resendretries = 3
sleeponretry = 100
To deploy your application you will need the following libraries:
kernel32.dll user32.dll gdi32.dll winspool.dll comdlg32.dll advapi32.dll
shell32.dll ole32.dll oleaut32.dll uuid.dll odbc32.dll odbccp32.dll
opends60.dll wldap32.dll activeds.dll adsiid.dll comsupp.dll
mqrt.dll rpcrt4.dll
The usual stuff. Probably the unusuall ones are the opends60.dll that is
required to support the 'clldaplib.dll' to be used by MS-SQL; the wldap32.dll
used to suport LDAP calls; mqrt.dll and rpcrt4.dll required for message
queueing.
After you get the required libraries in place you will need to setup the
client NT's, 98/95's hosts that will use the services. This is very easy,
just make sure that they point to your DNS where your directory service
host is recognized. Then make sure that you have the LDAP service declared.
To do so open it with the notepad.exe. This file is located at
"c:\WINNT\system32\drivers\etc\services" (on UNIX "/etc/services"). Make
sure that the following line is there:
ldap 389/tcp #Lightweight Directory Access Protocol
If you don't have a DNS or don't whant to depend on it, just open the
"c:\WINNT\system32\drivers\etc\hosts" (on UNIX "/etc/hosts") file with the
notepad and add a line with the name of the directory services host and its
IP address as follows:
195.203.205.100 ds-host-name
That's it. You should be set to run your application.
Don't get scared by the amount of comments in the *.cpp's and *.h'es. There
is a lot more comments than code, actually you will see a lot of those
comments as part of this document. I tend to write a lot into the code to be
self supported and informative so others spend less time trying to figure
out what I am doing.
Finally here is the list of stuff included here:
FILE | CONTENT |
ClLdap.cpp | Implementation code for ClLdap, LDAPEntry, LDIFEntries classes. |
ClLdap.h | The needed include file with ClLdap, LDAPEntry,
LDIFEntries and other required declarations. |
ClLdapUser.cpp | Implementation code for ClLdapUser classes. |
ClLdapUser.h | The needed include file with ClLdapUser declarations. |
LdapConsole.cpp | Here is a "main" for a console application that test most of the
ClLdap, ClLdapuser and other classes. Compile it and bind it with
other given files to test the classes. |
fstring.cpp | Not the most glorious implementation of a class to handle
in memory formated string. Still, if you whant speed and don't
care about a bit of funny syntax and code size. This sucker will
inline most of the string formating and will perform impresively
at run time. We did test it against similar formating using "sprintf"
and if you do invoke it a lot it may do more than 30% better,
beleave it or not. We did recreate about the same formating samples
in VB and got 60+ better performance using this class over VB
compiled code. Don't take this as good, do your own tests and get
to your own conclution. If your finding differ from mine please
inform me about them I will appretiate the info. |
fstring.h | The needed include file for the fstring.cpp. |
clldapservice.cpp | Sample 'cpp' that implements an NT/W2K service that drives the
reading of a queue. |
clldapservice.h | The companion include file for 'clldapservice.cpp'. |
ClLdap.html | This documentation. |
After the addition of the MSMQ support I am probably missing some cpp's and
include files. If you are really interested in taking a look at the message
queuing code tell use and as soon as we have some spare time I will put it
together for you. By reviewing the code you will see that there are some
support for XML stuff. We do light weight XML here with a class that we
implemented that will also port to UNIX. This is also missing on the code
package and soon will be included.
- Howes, T., "LDAP Programming Directory-Enabled Applications with
Lightwieght Directory Access Protocol", Macmillan (1997)
This is my personal favorite for LDAP stuff. Author do give lots of
samples and details about implementing LDAP apps.
- Kirkpatrick, G., "Active Directory Programming", SAMS (2000)
If you are looking for a mixture of Active Directory and LDAP stuff
this is the best reference that I have found. It provides you with
good overview of the Ms Active Directory implementation and how it
relates to LDAP. LDAP programming samples are a bit less detailed
than Mr. Howes still very usefull. This is my second favorite.
- Robinson, S., "Professional ADSI Programming", WROX (1999)
I did find nice information about available tools to browse and view
the directories on W2k. See tools listing below.
- Wilcox, M., "Implementing LDAP", WROX (1999)
A combo of methods to access LDAP on about every existing method.
-
http://www.umich.edu/~dirsvcs/ldap/doc/rfc/rfc1823.txt
Solid LDAP information and their API's from the University of Michigan.
-
http://www.ietf.org/html.charters/ldapext-charter.html
LDAP Extentions document with lots of other links to more LDAP related
info.
Also take a look at the MSDN Library since it has tons of info on ADSI /
LDAP / LDIF and directory services. To browse/view the Active Directory
take a look at the schema, its properties and entries the following are
great:
- Active Directory Browser (adsvw.exe)
This tool provides you to browse the directory and get
acquainted with the schema.
- ADSIEDIT (a MMC snap-in)
Provide a nice UI to edit entries by hand still it hardly
help in your understanding of schema internals.
Recalling that you are on your own, still if you feel that you need some
help drop in some e-mail at esob@openk.com or cram@openk.com.
I will appreciate any comment of the content and/or advice to improve the
quality of the code, documentation or what ever you think it can be improved
still I expect that they are constructive.