<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>LdapGuru  - all about LDAP, IT news, and IT art &#187; Manual</title>
	<atom:link href="http://www.ldapguru.com/tag/manual/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ldapguru.com</link>
	<description>Do it right the first time around.</description>
	<lastBuildDate>Wed, 23 Jun 2010 07:19:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Create an LDAP Address Book</title>
		<link>http://www.ldapguru.com/2009/10/create-an-ldap-address-book/</link>
		<comments>http://www.ldapguru.com/2009/10/create-an-ldap-address-book/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 13:45:18 +0000</pubDate>
		<dc:creator>ls</dc:creator>
				<category><![CDATA[Manual]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[ldap]]></category>

		<guid isPermaLink="false">http://ldapguru.php5.dev.justwebit.ru/?p=7</guid>
		<description><![CDATA[This article will attempt to demonstrate how to connect to an LDAP server using PHP. Specifically, the example given will connect to a public LDAP server and perform searches. This example closely mimics the way Netscape® Communicator 4.* uses its address book to connect to LDAP resources.
Introduction to LDAP
Many have probably heard much about LDAP, [...]]]></description>
			<content:encoded><![CDATA[<p>This article will attempt to demonstrate how to connect to an LDAP server using PHP. Specifically, the example given will connect to a public LDAP server and perform searches. This example closely mimics the way Netscape® Communicator 4.* uses its address book to connect to LDAP resources.<span id="more-7"></span></p>
<h4>Introduction to LDAP</h4>
<p>Many have probably heard much about LDAP, but have no idea what it is or how it works. I will not attempt to teach everything there is to know about LDAP, but here is a brief description of the protocol. LDAP is a protocol for distributing directory information to many different resources. Most commonly it is used as a centralized address book, but it can be much more powerful depending on an organization&#8217;s needs. LDAP in its most basic form is a standard way to connect to a database. The database is optimized for read queries. Thus, it retrieves information very quickly, in contrast to additions or updates which are slower. It is important to note that LDAP most often uses a hierarchal database, rather than a relational database to store data. Therefore, the structure is better represented with a tree than a table. As a result, SQL syntax will be rendered unusable. In short, LDAP is a fast way to retrieve centralized, static data containing information about people and/or resources.</p>
<h4>Requirements</h4>
<ul>
<li> PHP v.4 (previous versions may work but are untested) compiled with support for LDAP, I.E. <em>&#8211;with-ldap</em>.</li>
<li> Publically accessible LDAP directory. Two are provided in the example.</li>
</ul>
<h4>Overview of Example</h4>
<ol>
<li> Setup Public LDAP Server Information</li>
<li> Create LDAP Query</li>
<li> Connect to LDAP Server</li>
<li> Process Query if Connection Was Successful</li>
<li> Format Output</li>
<li> Close Connection</li>
<li> Make HTML Form for Search Interface</li>
<li> Echo Results</li>
</ol>
<h4>Setup Public LDAP Server Information</h4>
<p>The first thing we need to do is define all of the LDAP servers we might want to search. <em>&#8220;LDAP_NAME&#8221;</em><br />
= The name of the new LDAP entry. <em>&#8220;LDAP_SERVER&#8221;</em><br />
= The IP address or hostname of the new LDAP entry. <em>&#8220;LDAP_ROOT_DN&#8221;</em><br />
= The root distinguished name of the new LDAP entry.</p>
<pre class="brush: php">

&lt;?php
$LDAP_NAME[0] = &quot;Netscape Net Center&quot;;
$LDAP_SERVER[0] = &quot;memberdir.netscape.com&quot;;
$LDAP_ROOT_DN[0] = &quot;ou=member_directory,o=netcenter.com&quot;;
$LDAP_NAME[1] = &quot;Bigfoot&quot;;
$LDAP_SERVER[1] = &quot;ldap.bigfoot.com&quot;;
$LDAP_ROOT_DN[1] = &quot;&quot;;
//If no server chosen set it to 0
if(!$SERVER_ID) $SERVER_ID=0; ?&gt;;
</pre>
<h4>Create LDAP Query</h4>
<p>As mentioned previously, LDAP queries are not much like SQL queries. Therefore, the syntax may seem a bit limiting, but here is a basic example and one that works in this scenario.</p>
<pre class="brush: php">
//Create Query
$ldap_query = &quot;cn=$common&quot;;
</pre>
<p>In our example <em>&#8220;cn&#8221;</em><br />
is the attribute on which we are performing the search, and <em>$common</em><br />
is the search string variable from the search form. LDAP query syntax allows for wildcard matching using &#8216;*&#8217;. For example, &#8216;*stanley&#8217; will find &#8216;dan stanley&#8217;.</p>
<h4>Connect to LDAP Server</h4>
<p>The given function connects to an LDAP resource and assigns the connection link identifier to a variable, much like connecting to a regular database, like MySQL.</p>
<pre class="brush: php">
&lt;?php
//Connect to LDAP
$connect_id = ldap_connect($LDAP_SERVER[$SERVER_ID]); ?&gt;;
</pre>
<p>In our example, <em>&#8220;$connect_id&#8221;</em><br />
is the link identifier, <em>$LDAP_SERVER</em><br />
is the array of possible ldap servers, and <em>$SERVER_ID</em><br />
is the LDAP server variable from the search form.</p>
<h4>Process Query if Connection Was Successful</h4>
<p>If our connection was successful, we will have a valid LDAP link identifier and we can process the query.</p>
<pre class="brush: php">
&lt;?php
if($connect_id) {
//Authenticate
$bind_id = ldap_bind($connect_id);
//Perform Search
$search_id = ldap_search($connect_id, $LDAP_ROOT_DN[$SERVER_ID], $ldap_query);
//Assign Result Set to an Array
$result_array = ldap_get_entries($connect_id, $search_id);
} else {
//Echo Connection Error
echo &quot;Could not connect to LDAP server: $LDAP_SERVER[$SERVER_ID]&quot;; } ?&gt;;
</pre>
<p>Once we have established a connection to the LDAP services, we must identify ourselves. Most database connections with PHP send the username and password with the connection. However, with LDAP, credentials are unknown until a <em>bind</em><br />
is performed. In our example, <em>&#8220;$bind_id&#8221;</em><br />
is the bind link identifier. We are performing an anonymous bind to the public LDAP servers. Therefore, no argument is sent to <em>ldap_bind()</em><br />
accept the connection link identifier. After we have been authorized, via bind as anonymous, we perform the query using the <em>ldap_search()</em><br />
function. <em>$search_id</em><br />
is created and is our search link identifier. Then, we assign our result set to the variable <em>$result_array</em><br />
using the function <em>ldap_get_entries()</em>. This will allow us to sort the information in a logical manner for display.</p>
<h4>Format Output</h4>
<p>When an LDAP search is performed, the data is returned in whatever sequence it is found. In other words, there is not an easy way to sort the data like with a common SQL <em>ORDER BY</em><br />
statement. As well, many public LDAP directories do not have standard capitalization. Since the sort is based on the ASCII value of the strings, we must format the strings with all lowercase letters to appropriately alphabetize our output. It is important to note, that an LDAP result set is returned as a multi-dimensional array. Thus, at this point in our script <em>$result_array</em><br />
contains something like this:</p>
<pre class="brush: php">
$result_array[0][&quot;cn&quot;] [0] = &quot;Dannie Stanley&quot; [&quot;dn&quot;] [0] = &quot;uid=dannie,dc=spinweb.net&quot; [&quot;givenname&quot;][0] = &quot;Dannie&quot; [&quot;sn&quot;] [0] = &quot;Stanley&quot; [&quot;mail&quot;] [0] = &quot;danSPAM@spinweb.net&quot; $result_array[1][&quot;cn&quot;] [0] = &quot;Michael Reynolds&quot; [&quot;dn&quot;] [0] = &quot;uid=michael,dc=spinweb.net&quot; [&quot;givenname&quot;][0] = &quot;Michael&quot; [&quot;sn&quot;] [0] = &quot;Reynolds&quot; [&quot;mail&quot;] [0] = &quot;michaelSPAM@spinweb.net&quot;
</pre>
<p>The data is stored in this format because each attribute may have more than one value (IE a tree structure). For example, if my name is &#8216;Dannie,&#8217; yet everyone knows me as &#8216;Dan,&#8217; I could add an attribute to LDAP to store both representations of my given name like this:</p>
<pre class="brush: php">
$result_array[0][&quot;cn&quot;] [0] = &quot;Dannie Stanley&quot; [&quot;dn&quot;] [0] = &quot;uid=dannie,dc=spinweb.net&quot; [&quot;givenname&quot;][0] = &quot;Dannie&quot; [&quot;givenname&quot;][0] = &quot;Dan&quot; [&quot;sn&quot;] [0] = &quot;Stanley&quot; [&quot;mail&quot;] [0] = &quot;danSPAM@spinweb.net&quot;
</pre>
<p>For this search, we are only worried about the first value of every attribute so we will be using 0 as the index for each attribute, except for <em>dn</em><br />
(Distinguished Name), which contains only one value. Here is a brief list of attributes and their meaning: <em>&#8220;cn&#8221;</em><br />
= Common Name <em>&#8220;dn&#8221;</em><br />
= Distinguished Name <em>&#8220;givenname&#8221;</em><br />
= First Name <em>&#8220;sn&#8221;</em><br />
= Last Name <em>&#8220;mail&#8221;</em><br />
= Email Address</p>
<pre class="brush: php">
&lt;?php

//Sort results if search was successful
if($result_array)
{
for($i=0; $i&lt;count($result_array); $i++)
{ $format_array[$i][0] = strtolower($result_array[$i][&quot;cn&quot;][0]);
$format_array[$i][1] = $result_array[$i][&quot;dn&quot;];
$format_array[$i][2] = strtolower($result_array[$i][&quot;givenname&quot;][0]);
$format_array[$i][3] = strtolower($result_array[$i][&quot;sn&quot;][0]);
$format_array[$i][4] = strtolower($result_array[$i][&quot;mail&quot;][0]);
}
//Sort array
sort($format_array, &quot;SORT_STRING&quot;);
for($i=0; $i&lt;count($format_array); $i++)
{
$cn = $format_array[$i][0];
$dn = $format_array[$i][1];
$fname = ucwords($format_array[$i][2]);
$lname = ucwords($format_array[$i][3]);
$email = $format_array[$i][4];
if($dn &amp;amp;&amp;amp; $fname &amp;amp;&amp;amp; $lname &amp;amp;&amp;amp; $email)
{ $result_list .= &quot;&lt;
A HREF=&quot;ldap://$LDAP_SERVER[$SERVER_ID]/$dn&quot;&gt;$fname $lname&lt;/a&gt;&quot;;
$result_list .= &quot; &lt;&lt;a HREF=&quot;mailto:$email&quot;&gt;$email&lt;/a&gt;&gt;&lt;br /&gt; &quot;;
}
elseif
($dn &amp;amp;amp;&amp;amp;amp; $cn &amp;amp;amp;&amp;amp;amp; $email)
{ $result_list .= &quot;&lt;a HREF=&quot;ldap://$LDAP_SERVER[$SERVER_ID]/$dn&quot;&gt;$cn&lt;/a&gt;&quot;; $result_list .= &quot; &lt;&lt;a HREF=&quot;mailto:$email&quot;&gt;$email&lt;/a&gt;&gt;&lt;br /&gt; &quot;; } } }
else
{ echo &quot;Result set empty for query: $ldap_query&quot;; } ?&gt;
</pre>
<p>In our example, <em>$format_array</em><br />
is our new array which contains the query results in a format optimized for output. First, we loop through every element of the <em>$result_array</em><br />
and assign it to a two-dimensional array for sorting purposes. At the same time we are using the <em>strtolower()</em><br />
function to make all values lower-case. Second, we sort the array using a handy little search algorithm provided by PHP called <em>sort()</em>. The first argument is the array. The second is what type of sorting to perform, as defined by the PHP documentation. Since we are sorting by string, we use <em>&#8220;SORT_STRING&#8221;</em>. Third, we loop through the newly formatted array and assign it to an output string named <em>$result_list</em><br />
that contains the HTML representation of the data. It is important to note that I have used the ldap URL format for the hyper-links. An example of this looks something like this: HREF=&#8221;ldap://ldap.domain.net/uid=dannie,dc=domain.net&#8221;.</p>
<h4>Close Connection</h4>
<p>Now that we have all of our data contained in <em>$result_list</em>, we can safely disconnect from the LDAP connection.</p>
<pre class="brush: php">
&lt;?php

//Close Connection
ldap_close($connect_id); ?&gt;; </pre>
<h4>Make HTML Form for Search Interface</h4>
<p>Finally, we get to the HTML output of the script. This set of code prints out the form that is used for performing the searches.</p>
<pre class="brush: php">
&lt;?php

//Make Form
echo &quot;&lt;center&gt;&lt;form ACTION=&quot;$PHP_SELF&quot; METHOD=&quot;GET&quot;&gt;&quot;;
echo &quot;Search in:&lt;select NAME=&quot;SERVER_ID&quot;&gt;&quot;;
//Loop Through and Create SELECT OPTIONs
for($i=0; $i&lt;count($LDAP_NAME); $i++) echo &quot;&lt;option VALUE=&quot;$i&quot;&gt;&quot;.$LDAP_NAME[$i].&quot;&lt;/option&gt;&quot;;
echo &quot;&lt;/select&gt;&lt;br /&gt;&quot;; echo &quot;Search for:&lt;input TYPE=&quot;text&quot; NAME=&quot;common&quot;&gt;&quot;;
echo &quot;&lt;input TYPE=&quot;submit&quot; NAME=&quot;lookup&quot; VALUE=&quot;go&quot;&gt;&lt;br /&gt;&quot;;
echo &quot;(You can use * for wildcard searches, ex. * Stanley will find all Stanleys)&lt;br /&gt;&quot;; echo &quot;&lt;/form&gt;&lt;/center&gt;&quot;; ?&gt;
</pre>
<p>The only portions of this code that is interpreted is <em>$PHP_SELF</em><br />
which is a global constant for the name of the script itself and the loop that creates the SELECT box from our <em>$LDAP_NAME</em><br />
variable.</p>
<h4>Echo Results</h4>
<p>Now that all of the work has been done, we print out the result set. If no results were returned, a message is given stating the same.</p>
<pre class="brush: php">
&lt;?php

//Echo Results
if($result_list) {
echo &quot;&lt;center&gt;&lt;table BORDER=&quot;1&quot; CELLSPACING=&quot;0&quot; CELLPADDING=&quot;10&quot; BGCOLOR=&quot;#FFFFEA&quot; WIDTH=&quot;450&quot;&gt;&lt;tr&gt;&lt;td&gt;$result_list&lt;/td&gt;&lt;/tr&gt; &lt;/table&gt;&lt;/center&gt;&quot;; } else echo &quot;No Results&quot;; ?&gt;</pre>
<h4>Source Code</h4>
<p>Here is the complete source code. Simply cut and paste this into a valid HTML document and give it a try.</p>
<pre class="brush: php">
&lt;?php

$LDAP_NAME[0] = &quot;Netscape Net Center&quot;;
$LDAP_SERVER[0] = &quot;memberdir.netscape.com&quot;;
$LDAP_ROOT_DN[0] = &quot;ou=member_directory,o=netcenter.com&quot;; $LDAP_NAME[1] = &quot;Bigfoot&quot;;
$LDAP_SERVER[1] = &quot;ldap.bigfoot.com&quot;;
$LDAP_ROOT_DN[1] = &quot;&quot;;
//If no server chosen set it to 0
if(!$SERVER_ID) $SERVER_ID=0;
//Create Query
$ldap_query = &quot;cn=$common&quot;;
//Connect to LDAP
$connect_id = ldap_connect($LDAP_SERVER[$SERVER_ID]);
if($connect_id) {
//Authenticate
$bind_id = ldap_bind($connect_id);
//Perform Search
$search_id = ldap_search($connect_id, $LDAP_ROOT_DN[$SERVER_ID], $ldap_query);
//Assign Result Set to an Array
$result_array = ldap_get_entries($connect_id, $search_id); } else {
//Echo Connection Error
echo &quot;Could not connect to LDAP server: $LDAP_SERVER[$SERVER_ID]&quot;; }
//Sort results if search was successful
if($result_array) { for($i=0; $i&lt;count($result_array); $i++) {
$format_array[$i][0] = strtolower($result_array[$i][&quot;cn&quot;][0]); $format_array[$i][1] = $result_array[$i][&quot;dn&quot;]; $format_array[$i][2] = strtolower($result_array[$i][&quot;givenname&quot;][0]); $format_array[$i][3] = strtolower($result_array[$i][&quot;sn&quot;][0]); $format_array[$i][4] = strtolower($result_array[$i][&quot;mail&quot;][0]); }
//Sort array
sort($format_array, &quot;SORT_STRING&quot;); for($i=0; $i&lt;count($format_array); $i++) { $cn = $format_array[$i][0]; $dn = $format_array[$i][1]; $fname = ucwords($format_array[$i][2]); $lname = ucwords($format_array[$i][3]); $email = $format_array[$i][4]; if($dn &amp;amp;amp;&amp;amp;amp; $fname &amp;amp;amp;&amp;amp;amp; $lname &amp;amp;amp;&amp;amp;amp; $email) {
$result_list .= &quot;&lt;a HREF=&quot;ldap://$LDAP_SERVER[$SERVER_ID]/$dn&quot;&gt;$fname $lname&lt;
/A&gt;&quot;; $result_list .= &quot; &lt;&lt;a HREF=&quot;mailto:$email&quot;&gt;$email&lt;/a&gt;&gt;&lt;br /&gt; &quot;; } elseif($dn &amp;amp;amp;&amp;amp;amp; $cn &amp;amp;amp;&amp;amp;amp; $email) {
$result_list .= &quot;&lt;a HREF=&quot;ldap://$LDAP_SERVER[$SERVER_ID]/$dn&quot;&gt;$cn&lt;/a&gt;&quot;; $result_list .= &quot; &lt;&lt;a HREF=&quot;mailto:$email&quot;&gt;$email&lt;/a&gt;&gt;&lt;br /&gt; &quot;; } } } else {
echo &quot;Result set empty for query: $ldap_query&quot;; }
//Close Connection
ldap_close($connect_id);
//Make Form
echo &quot;&lt;center&gt;&lt;form ACTION=&quot;$PHP_SELF&quot; METHOD=&quot;GET&quot;&gt;&quot;;
echo &quot;Search in:&lt;select NAME=&quot;SERVER_ID&quot;&gt;&quot;;
//Loop Through and Create SELECT OPTIONs
for($i=0; $i&lt;count($LDAP_NAME); $i++)
echo &quot;&lt;option VALUE=&quot;$i&quot;&gt;&quot;.$LDAP_NAME[$i].&quot;&lt;/option&gt;&quot;; echo &quot;&lt;/select&gt;&lt;br /&gt;&quot;; echo &quot;
Search for:&lt;input TYPE=&quot;text&quot; NAME=&quot;common&quot;&gt;&quot;; echo &quot;&lt;input TYPE=&quot;submit&quot; NAME=&quot;lookup&quot; VALUE=&quot;go&quot;&gt;&lt;br /&gt;&quot;; echo &quot;(You can use * for wildcard searches, ex. * Stanley will find all Stanleys)&lt;br /&gt;&quot;; echo &quot;&lt;/form&gt;&lt;/center&gt;&quot;;
//Echo Results
if($result_list) { echo &quot;&lt;center&gt;&lt;table BORDER=&quot;1&quot; CELLSPACING=&quot;0&quot; CELLPADDING=&quot;10&quot; BGCOLOR=&quot;#FFFFEA&quot; WIDTH=&quot;450&quot;&gt;&lt;tr&gt;&lt;td&gt;$result_list&lt;/td&gt;&lt;/tr&gt; &lt;/table&gt;&lt;/center&gt;&quot;; } else echo &quot;No Results&quot;; } ?&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.ldapguru.com/2009/10/create-an-ldap-address-book/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LDAP Login Example</title>
		<link>http://www.ldapguru.com/2009/08/ldap-login-example/</link>
		<comments>http://www.ldapguru.com/2009/08/ldap-login-example/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 13:43:43 +0000</pubDate>
		<dc:creator>ls</dc:creator>
				<category><![CDATA[Manual]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[ldap]]></category>

		<guid isPermaLink="false">http://ldapguru.php5.dev.justwebit.ru/?p=3</guid>
		<description><![CDATA[This app provides a simple example of login authentication using Netscape&#8217;s LDAP Java API.
Release Notes
For Solaris, make sure to compile on the deployed server to ensure all necessary classes are                          [...]]]></description>
			<content:encoded><![CDATA[<p>This app provides a simple example of login authentication using Netscape&#8217;s LDAP Java API.<span id="more-36"></span></p>
<h2>Release Notes</h2>
<p>For Solaris, make sure to compile on the deployed server to ensure all necessary classes are                           included in the CLASSPATH.</p>
<p>The example contains one AppLogic, Login.java (plus the standard base classes Session and                           BaseAppLogic), and two HTML files: LDAPLogin.html and wrongLogin.html. The login page,                           LDAPLogin.html, lets the user enter a user name and password, which are used to authenticate                           the user on the LDAP server. If successful, the user&#8217;s LDAP attributes are streamed out to the                           client. (In a real app, this would probably call a method to extract a user ID or role to save to the                           session, and then call another App Logic that goes into the application itself.) If the login is not                           successful, then the user is redirected to wrongLogin.html and allowed to try again, up to a                           maximum number of failures, after which all login attempts will be refused, until the user&#8217;s                           session expires.</p>
<p>In addition to the standard execute() and guid() method, the Login AppLogic contains the                           following methods:</p>
<ul>
<li>maxFailuresReached: determines if the user&#8217;s maximum number of failed logins has been                                reached.</li>
<li>getDN: gets the LDAP distinguished name for the specified user</li>
<li>authenticateUser: attempts to authenticate the user with the specified password on the                                specified LDAP server.To get this example working you need to do the following:</li>
<li>Put the LDAP JAR file, LDAPJDK.JAR, on your NAS machine, and add it to your system                                CLASSPATH.</li>
<li>Because Java classes with native methods must be loaded by the default Java VM                                classloader, aka the &#8216;primordial&#8217; classloader, instead of the KIVA classloader, you must                                edit a filter list in the registry. Start up kreg tool (Regedit on NT:                                HKEY_LOCAL_MACHINE).Navigate in the tree to the following key&#8230;SOFTWARE/KIVA/Enterprise/2.0/CCS0/SYSTEM_JAVAEdit the GX_CLASSPATH_CORE value. It&#8217;s default installed value should be&#8230;java.;gx.;com.kivasoft.Add &#8220;netscape.&#8221; so that it now looks like:java.;gx.;com.kivasoft.;netscape.</li>
<li>Then restart your Netscape Application Server.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.ldapguru.com/2009/08/ldap-login-example/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Searching LDAP Servers with Net::LDAP</title>
		<link>http://www.ldapguru.com/2009/07/searching-ldap-servers-with-netldap/</link>
		<comments>http://www.ldapguru.com/2009/07/searching-ldap-servers-with-netldap/#comments</comments>
		<pubDate>Tue, 21 Jul 2009 13:53:01 +0000</pubDate>
		<dc:creator>ls</dc:creator>
				<category><![CDATA[Manual]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[ldap]]></category>

		<guid isPermaLink="false">http://ldapguru.php5.dev.justwebit.ru/?p=20</guid>
		<description><![CDATA[The Lightweight Directory Access Protocol (LDAP) is the Internet standard for directory services. Perl provides one of the best tools you can use to build LDAP-based applications. In this article we&#8217;re going to learn how to use Perl to search LDAP servers.
Why Do We Need LDAP?
A directory service is specialized database that has been optimized [...]]]></description>
			<content:encoded><![CDATA[<p>The Lightweight Directory Access Protocol (LDAP) is the Internet standard for directory services. Perl provides one of the best tools you can use to build LDAP-based applications. In this article we&#8217;re going to learn how to use Perl to search LDAP servers.<span id="more-20"></span></p>
<p>Why Do We Need LDAP?</p>
<p>A directory service is specialized database that has been optimized for read access. The data that is contained in a directory service is usually the type of data that is accessed more often than it&#8217;s updated (things like password databases, DNS tables, etc). A directory is usually organized in a hierarchical fashion as opposed to a relational format. Finally a directory service generally is designed so that it can be easily distributed across different servers.</p>
<p>Directory services provide us with the ability to provide all sorts of services such as a network email address book, ability to provide single sign-on or manage networked devices like printers.</p>
<p>There several different directory service protocols, such as NIS/NIS+, Novell&#8217;s NDS and X.500. LDAP is an open-standard (X.500 is also an open standard and is the forebearer to LDAP, but it wasn&#8217;t widely implemented because of its roots in the OSI protocol) and has gained popularity because all of the major proprietary directory service vendors (Sun, Novell, Microsoft) have all released versions of their directory service products that can &#8217;speak&#8217; LDAP.</p>
<p>The most important thing LDAP provides for us is the ability to centralize the management of data that has been decentralized across the enterprise. This data has been decentralized because it was too hard to get access to it if it was managed centrally (e.g. in a mainframe). The reason why we want to move this data back to the &#8216;center&#8217; is that in a networked environment, much of the information we need to accurately manage our connected world is stored in various data stores that aren&#8217;t easily accessed via a standard management interface.</p>
<p>Take user databases for example.</p>
<p>At the University of North Texas we created the concept of an &#8216;enterprise userid&#8217; that could form the basis of single-signon. We came up with this specification about 8 years ago and in Academic Computing Services we have implemented the system. However, we have never had a real good idea of who should have access and when their access should be turned off (e.g. they graduated, left for a different job, etc). The best we could do was attempt to &#8216;read the tea leaves&#8217; that we got in the form of a flat-file from our ID Card Office. In reality this information was actively maintained in a particular database in the mainframe, but we didn&#8217;t have a way to get access to the data in the mainframe from our Solaris boxes (CORBA was out of the question because our mainframe database doesn&#8217;t support a CORBA interface, it&#8217;s a long story <img src='http://www.ldapguru.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p>
<p>Another problem was that other systems on campus, such as our Novell group wanted to be able to use our EUID database so that students could have the same userid on our UNIX systems and Novell systems.</p>
<p>Finally, in ironic twist, the Administrative Computing Services group (aka &#8216;the mainframe programmers&#8217;) wanted access to our student email addresses so that they could send them official messages like financial aid statements and grades.</p>
<p>Thus we decided to build an LDAP server since it would allow us to give access to this type of data via a standard, secure, open, Internet-based protocol.</p>
<p>Now the mainframe will provide us with a better feed (via a CORBA like product called EntireBroker) so that we have a better idea of who should &amp; shouldn&#8217;t have access to computing services. We are also able to provide extra services such as automatically creating &#8216;bulk mail&#8217; services for our administration and faculty. For example select members of the administration can send messages to students based on a particular query (e.g. all freshmen biology majors) and faculty can send messages to students in their courses. We use the LDAP server to answer these queries and to control access to who can send these messages.</p>
<p>Onto Perl!</p>
<p>Ok, you&#8217;re now wondering what does this have to do with Perl? Well a couple of things. One is that Perl is the de-facto standard for CGI programming and one of the most popular things to do with LDAP is to provide a Web interface to its information (generally in the form of an enterprise email/phone search). Second, LDAP is often populated with data from legacy information and Perl is one of the best tools you can use to move data from one source to another (in particular if you have to do any type of data normalization).</p>
<p>The rest of this article we are going to cover how to search an LDAP server using the Net::LDAP module.</p>
<p>First A Brief Intro to LDAP</p>
<p>Before we dive directly into LDAP programming, there are a few other things you need to know.</p>
<p>First the term &#8216;LDAP&#8217; can mean one of four things. One is a data model. The data model defines what an LDAP entry looks like. Second, there is the naming model which defines how LDAP entries are named. Third is the security model which defines how to protect your data in an LDAP server. Finally there is an access model. The access model defines how LDAP clients and servers talk to each other.</p>
<p>The data stored in the LDAP server (as specified by the data model) are stored as entries. Entries have a collection of attributes which are represented as name-value pairs. Each attribute can have one or more values associated with it. The values can be text or binary. There is a special attribute called the Distinguished Name (DN) which must be unique within its particular directory server. A DN is made up of attributes found in the entry. By reading a DN you can determine where that entry fits into the directory server, just like you can figure out where a file fits on a hard-drive by looking at its path-name. One key difference between a DN and a path-name is that a DN goes from leaf to root while a path-name goes from root to leaf.</p>
<p>Here is an example of a DN:</p>
<pre class="brush: php">

uid=mewilcox,ou=people,o=airius.com
</pre>
<p>Check the resources section at the end of this article to learn where you can find more about LDAP.</p>
<p>Why Net::LDAP</p>
<p>Now we&#8217;ll actually start to do some programming. In our examples here we are going to be using Graham Barr&#8217;s Net::LDAP module. There are two other LDAP modules, Net::LDAPapi (no longer actively maintained) and Netscape&#8217;s PerLDAP which are Perl wrappers around the Netscape C LDAP API.</p>
<p>The Net::LDAP module is written in pure Perl, no C compiler required which makes this module incredibly portable. All you need is a copy of Perl 5.004 or later and you&#8217;re ready to go. On the Net::LDAP mailing list we&#8217;ve heard from people who have used the module on everything from MacPerl, to Windows, to all flavors of Unix and I&#8217;m pretty sure someone has even had a copy running on a mainframe somewhere all without having to recompile or port any code!</p>
<p>Assuming that you have the Perl lib-net bundle (required to do any network programming in Perl &amp; I&#8217;m pretty sure a standard module set these days), then Net::LDAP only requires 2 modules, Convert::BER (soon to replaced by Convert::ASN1, I&#8217;ll explain more about this later) and Net::LDAP. Optionally you can also install Digest::MD5 (used for SASL-CRAM MD5 authentication) and the URI module (used to parse LDAP URLs). All of these modules are available on CPAN.</p>
<p>After you have downloaded the modules, they are a pretty straightforward install. If you are going to use the optional modules, you should install those modules first. Then you must install Convert::BER followed by Net::LDAP.</p>
<p>All of the modules follow a similar install procedure:</p>
<p>1. unpack the module<br />
2. cd into their unpacked directory (e.g. perl-ldap-0.14)<br />
3. type perl Makefile.PL<br />
4. type make<br />
5. type make test<br />
6. type make install</p>
<p>Now you are ready to start hacking away.</p>
<p>Examples</p>
<p>In this article we are going to confine our examples to just searching an LDAP server. The steps to do this are:</p>
<p>1. load the Net::LDAP module<br />
2. connect to the LDAP server<br />
3. bind (authenticate) to the LDAP server<br />
4. define what attributes we wish to have returned from the server<br />
5. define a search base<br />
6. define a search scope<br />
7. define a search filter<br />
8. perform the LDAP search<br />
9. display the results</p>
<p>To connect to an LDAP server all you have to do is:</p>
<pre class="brush: php">

my $ldap = new Net::LDAP( &#039;hostname&#039;);
</pre>
<p>The next step is you must authenticate your connection to the LDAP server via an LDAP bind. The LDAP bind operation associates a client connection to a particular entry in the LDAP server. While LDAP can support a variety of authentication mechanisms including digital certificates and Kerberos (via the SASL mechanism), we are only going to concern ourselves with simple authentication which requires the Distinguished Name (DN) of an entry and a password. A DN is the unique name each LDAP entry has. Under LDAP you can authenticate to the server anonymously, this is very common for when you are using LDAP to publish a public directory service and you don&#8217;t want to issue accounts to the millions of potential users on the Internet. Generally, anonymous connections only have a very limited access to the server.</p>
<p>Here is how you authenticate as a particular user:</p>
<pre class="brush: php">

my $mesg = $ldap-&amp;gt;bind(&#039;uid=mewilcox,ou=people,o=airius.com&#039;, password =&amp;gt; &#039;password&#039;);
</pre>
<p>Here is how to authenticate anonymously:</p>
<pre class="brush: php">

my $mesg = $ldap-&amp;gt;bind();
</pre>
<p>Nearly every function in Net::LDAP will return an Net::LDAP::Message object which I&#8217;ve put in my example here in the $mesg variable. You can use the Net::LDAP::Message::code function to check the LDAP return code. This will tell you if you successfully authenticated or not. Here is the code I use:</p>
<pre class="brush: php">

die (&quot;failed to bind with &quot;,$mesg-&amp;gt;code(),&quot; &quot;) if $mesg-&amp;gt;code();
</pre>
<p>The code I just showed you works because under LDAP all non-successful operations will have an LDAP result code of 1 or greater. Since under Perl false is zero and any non-zero values is true, the die function will only be called if the LDAP operation did not succeed.</p>
<p>Our next step is to build our search function. A search requires three elements, a search base, a search scope and a filter.</p>
<p>The search base is the DN of the entry you want to be begin to your search at.</p>
<p>The search scope defines how much of the LDAP server you want to search at one time. There are three scopes. The most common scope is sub, which means start the search at the search base and search all entries below the base, but include the base entry in your search. There is also the base scope which starts at the search base and searches all entries below the base, but does not include the base entry. Finally there is a scope of one, which only searches the entry specified by the search base.</p>
<p>In a search filter you specify the attributes and values you want to match to declare a successful search. Whether a search is case sensitive or not depends upon what attribute you are searching. When you configure an LDAP server you tell it which attributes are case-sensitive and which ones are not.</p>
<p>A search filter can be very simple like this one:</p>
<p>(sn = Wilcox)</p>
<p>Which says find all entries that have a sn attribute that contains a value of Wilcox.</p>
<p>You can also do wildcard searches like this:</p>
<p>(sn = Wil*)</p>
<p>Which will find all entries that have an sn attribute that contains a value that starts with Wil.</p>
<p>You can also do boolean searches. AND is represented by the &amp;. OR is represented by the | and NOT is represented by the !. You can combine AND and OR with different filters, but NOT can only apply to one filter.</p>
<p>For example here&#8217;s how you can say &#8216;give me all entries who have a last name of Wilcox and work in Accounting (where they work is specified in the ou attribute)&#8217;.</p>
<p>(&amp;(sn = Wilcox) (ou=Accounting))</p>
<p>Or we can say &#8216;give me all people who work in Accounting or Engineering&#8217;.</p>
<p>(|(ou=Accounting)(ou=Engineering))</p>
<p>Or we can say &#8216;give me all entries except for those who work in Accounting&#8217;.<br />
(!(ou=Accounting))</p>
<p>Or we can combine some of these like this.<br />
(&amp;(sn=Wilcox)(|(ou=Accounting)(ou=Engineering)))</p>
<p>Here is an example search in Net::LDAP:</p>
<pre class="brush: php">

$mesg = $ldap-&amp;gt;search(
base =&amp;gt; &#039;ou=people,o=airius.com&#039;,
scope =&amp;gt; &#039;sub&#039;,
filter =&amp;gt; &#039;(objectclass=*)&#039;
);
</pre>
<p>The searchbase is ou=people,o=airius.com, the scope is sub and the filter is (objectclass=*). This last filter says return all entries that have a value in their objectclass attribute. The objectclass attribute is the only attribute guranteed to be in each entry in the LDAP server (the DN is a special attribute is not really part of the entry). An objectclass attribute is used by the LDAP server to determine what attributes a particular LDAP entry is required to have and which ones it is simply allowed to have.</p>
<p>Again we can check for the success of the operation by using the returned Net::LDAP::Message object.</p>
<p>die (&#8221;search failed with &#8220;,$mesg-&gt;code(),&#8221; &#8220;) if $mesg-&gt;code();</p>
<p>The entries returned from a search are also contained in our Net::LDAP::Message object.</p>
<p>We can get them one at a time using either Net::LDAP::Message::shift_entry or Net::LDAP::Message::pop_entry. Or we can get all of the entries as once using Net::LDAP::Message::entries. An LDAP entry is stored in a Net::LDAP::Entry object.</p>
<p>I generally dump my entries using code that looks something like this:</p>
<pre class="brush: php">

while (my $entry = $mesg-&amp;gt;shift_entry())
{
$entry-&amp;gt;dump();
}
</pre>
<p>The easiest way to display the results of a search is using the Net::LDAP::Entry::dump method which simply dumps the contents of an entry to STDOUT in LDIF format. The LDAP Data Interchange Format is the standard way to display LDAP data in a human readable format. There is a newer XML standard, DSML, but it hasn&#8217;t been widely implemented anywhere (yet).</p>
<p>If you use simplesearch.pl or bindedsimplesearch.pl to search your local LDAP server, you will see results that look something like this:</p>
<pre class="brush: php">

dn:uid=scarter, ou=People, o=airius.com
cn: Sam Carter
sn: Carter
givenname: Sam
objectclass: top
person
organizationalPerson
inetOrgPerson
ou: Accounting
People
l: Sunnyvale
uid: scarter
mail: scarter@airius.com
telephonenumber: +1 408 555 4798
facsimiletelephonenumber: +1 408 555 9751
roomnumber: 4612
</pre>
<p> &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>One thing you&#8217;ll likely notice when you run these two example scripts is that the bindedsimplesearch.pl results will display more attributes than the simplesearch.pl. This is because LDAP uses ACLs to control access to the attributes stored in its entries.  By properly configuring ACLs you can easily provide different &#8216;views&#8217; to entries stored in the LDAP server.</p>
<p>While these scripts are handy for testing out LDAP servers, they don&#8217;t work real well when you want to either change how your results are displayed or when you only want to retrieve a select number of attributes from an entry.</p>
<p>One way to limit the attributes you are seeing is by telling the LDAP server to only retrieve particular attributes. This requires two steps.</p>
<p>First you must specify the attributes via an anonymous array like this:</p>
<p>my $attrs = ['cn','mail'];</p>
<p>Second you must pass these attributes to the LDAP server in the search method like this:</p>
<pre class="brush: php">

$mesg = $ldap-&amp;gt;search(
base =&amp;gt; &#039;ou=people,o=airius.com&#039;,
scope =&amp;gt; &#039;sub&#039;,
filter =&amp;gt; &#039;(objectclass=*)&#039;,
attrs =&amp;gt; $attrs,
);
</pre>
<p>Now lets say that instead of dumping the attributes out to STDOUT, you would like to dump the results out to a text file. While it is true you could simply pipe the output from the earlier scripts into a text file, there is a cleaner solution in Net::LDAP with the Net::LDAP::LDIF class. The Net::LDAP::LDIF class lets you read and write LDIF files. What&#8217;s great about this is that LDIF allows us to specify LDAP update commands in the LDIF itself and by using Net::LDAP::LDIF we can take other LDIF files and with relative ease, modify our LDAP server without having to write a lot of code.</p>
<p>However, for now we just simply want to write our search results out to a file. First you must directly import Net::LDAP::LDIF with use Net::LDAP::LDIF because Net::LDAP doesn&#8217;t automatically load the module into your program.</p>
<p>Next you must obtain an instance of the Net::LDAP::LDIF class like this:</p>
<pre class="brush: php">

my $ldif = new Net::LDAP::LDIF (&#039;example.ldif&#039;,&#039;w&#039;) || die (&quot;failed to open example.ldif. $! &quot;);

This will open a new file named example.ldif that is ready to be written to.

Third instead of simply dumping the results to the screen with Net::LDAP::Entry::dump, you write them to the LDIF file like this:

$ldif-&amp;gt;write($entry);

Finally when you are done with the LDIF file you can close it by:

$ldif-&amp;gt;done();
</pre>
<p>But what about if you don&#8217;t want plain old LDIF. What if you wanted to be ahead of the curve and actually write your results out in DSML so that you could other things with it (e.g. apply style sheets to it for displaying in a browser or feed another datasource that only takes XML tagged data).</p>
<p>Here&#8217;s how you could do that.</p>
<p>First we must open a file to print our XML data out to.<br />
open(DSML,&#8221;&gt;example.xml&#8221;) || die(&#8221;failed to open example.xml.$! &#8220;);</p>
<p>Second we print out the first lines of our XML file.</p>
<p>print DSML &#8221; &#8220;;<br />
print DSML &#8221; &#8220;;</p>
<p>Next we perform a search on the server and retrieve each entry one by one. Instead of dumping the entry to STDOUT or into a LDIF object, we are going to retrieve each of the entry&#8217;s attributes and values so that we can transform them into DSML entities.</p>
<p>Our first attribute we must get is the DN of the entry and print it out as DSML like this:<br />
my $dn = $entry-&gt;dn();<br />
print DSML &#8221; &#8220;;</p>
<p>Next we get an array of the attributes stored in the entry.<br />
#get a list of attributes<br />
my @attributes = $entry-&gt;attributes;</p>
<p>Our next trick is to step through each attribute, get its values from the entry and then print out the resulting DSML.</p>
<pre class="brush: php">

for my $attribute (@attributes)
{
#in DSML objectclasses are written differently than other attributes
if ( $attribute eq &#039;objectclass&#039;)
{
print DSML &quot; &quot;;

my $values = $entry-&amp;gt;get($attribute);

for my $v (@{$values})
{
print DSML &quot;$v &quot;;
}
print DSML &quot; &quot;;
}
else
{
#Rabbit
my $values = $entry-&amp;gt;get($attribute);

print DSML &quot; &quot;;
for my $v (@{$values})
{
print DSML &quot;$v &quot;;
}
print DSML &quot; &quot;;
}
}

Finally we must close off all of our DSML entry tags.
print DSML &quot; &quot;;
}
print DSML &quot; &quot;;
</pre>
<p>The final steps are to close the DSML filehandle and the LDAP connection (via Net::LDAP::unbind, something you should do in all of your LDAP programs, though Perl will take care of it for you if you forget).</p>
<p>The resulting program, dsmlsearch.pl will print out an entry that looks something like this:</p>
<p>Sam Carter</p>
<p>scarter@airius.com</p>
<p>top<br />
person<br />
organizationalPerson<br />
inetOrgPerson</p>
<p>Our final example will show you how to use the callback parameter of Net::LDAP. A callback is simply a subroutine that is called everytime a Net::LDAP::Message object is created during a search. The reason you want to go through the trouble of doing such a thing is that it can make your program appear to run faster, which is important when you&#8217;re using Net::LDAP to build a Web application. The reason why it appears to run faster is that you can start to work on the results as soon as they appear instead of having to wait for all of the results in a non-callback approach.</p>
<p>To make it easier to compare the code, I&#8217;ve rewritten the simplesearch.pl script to use a callback in a script called callbacksearch.pl.</p>
<p>Here&#8217;s the callback routine.</p>
<pre class="brush: php">

sub callback {
my ($mesg,$entry) = @_;

$entry-&amp;gt;dump() if ref $entry eq &#039;Net::LDAP::Entry&#039;;
}
</pre>
<p>Now here&#8217;s the modified search method.</p>
<pre class="brush: php">

$mesg = $ldap-&amp;gt;search(
base =&amp;gt; &#039;ou=people,o=airius.com&#039;,
scope =&amp;gt; &#039;sub&#039;,
filter =&amp;gt; &#039;(objectclass=*)&#039;,
callback =&amp;gt; &amp;amp;amp;callback
);
</pre>
<p>Convert::ASN1</p>
<p>The most common complaint about Net::LDAP is that it&#8217;s &#8216;too slow&#8217;. Of course this is all a matter of perception. For all of the applications that I have written, Net::LDAP&#8217;s performance has been more than adequate.  And any of it&#8217;s inefficiencies have been either covered up by network latency on the client&#8217;s connection or have been minor compared to the headache of compiling and installing a C based Perl module (especially if you must compile the C API from scratch). Another great thing about Net::LDAP is that people helping to contribute development represent users of all of the popular LDAP servers out there to help make sure that Net::LDAP stays vendor nuetral.</p>
<p>Net::LDAP will always be a bit slower than it&#8217;s C wrapper counterparts because it is pure Perl. Occasionally users have asked Graham to completely rewrite the API in C so that it will speed it up. But as Graham has pointed out, this isn&#8217;t necessary. The bulk of the work during any LDAP operation is in the encoding and decoding of the ASN.1 op-codes that LDAP uses to conduct its operations (occasionally LDAP does show its X.500/OSI roots). These methods have been carried out in the past by a module named Convert::BER (BER stands for Basic Encoding Rules, which is the encoding set LDAP uses for ASN.1). Graham has recently written a new module called Convert::ASN1. This module (still in pure Perl!) has already doubled the performance of Net::LDAP (at least in the development releases Graham and I have been testing out). He might even have a release on CPAN of Net::LDAP that uses Convert::ASN1 by the time you are reading this.</p>
<p>Graham also has written Convert::ASN1 in a way that will make it easier to put in a XS module so that those of us who can/want a C based back-end can have one, once such a module is written.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ldapguru.com/2009/07/searching-ldap-servers-with-netldap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
