Active Directory/Search for a user: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Wren)
(Added FreeBASIC)
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
[[Category:Active Directory]]
{{task|Programming environment operations}}
{{task|Programming environment operations}}


Line 4: Line 5:


=={{header|C}}==
=={{header|C}}==
<lang C>#include <ldap.h>
<syntaxhighlight lang="c">#include <ldap.h>


char *name, *password;
char *name, *password;
Line 24: Line 25:


ldap_msgfree(*result); /* free messages */
ldap_msgfree(*result); /* free messages */
ldap_unbind(ld); /* disconnect */</lang>
ldap_unbind(ld); /* disconnect */</syntaxhighlight>


=={{header|D}}==
=={{header|D}}==
Based on dopenldap.
Based on dopenldap.
<syntaxhighlight lang="d">
<lang d>
import openldap;
import openldap;
import std.stdio;
import std.stdio;
Line 57: Line 58:
}
}
</syntaxhighlight>
</lang>


=={{header|Eiffel}}==
=={{header|Eiffel}}==
Line 63: Line 64:


Moreover, strings in Eiffel are objects and cannot be directly passed to the Windows OS. As such, they need to undergo a format change through the facilities of a WEL_STRING, which makes the appropriate structure conversion.
Moreover, strings in Eiffel are objects and cannot be directly passed to the Windows OS. As such, they need to undergo a format change through the facilities of a WEL_STRING, which makes the appropriate structure conversion.
<syntaxhighlight lang="eiffel">
<lang Eiffel>
feature -- Validation
feature -- Validation


Line 76: Line 77:
Result := cwel_is_credential_valid (l_domain.item, l_username.item, l_password.item)
Result := cwel_is_credential_valid (l_domain.item, l_username.item, l_password.item)
end
end
</syntaxhighlight>
</lang>


Because Active Directory is a Windows OS facility, in Eiffel we must use the WEL (Windows Eiffel Library) components. Thus, the code above is not cross-platform. Moreover, the call to `cwel_is_credential_valid' is shown below:
Because Active Directory is a Windows OS facility, in Eiffel we must use the WEL (Windows Eiffel Library) components. Thus, the code above is not cross-platform. Moreover, the call to `cwel_is_credential_valid' is shown below:


<syntaxhighlight lang="eiffel">
<lang Eiffel>
cwel_is_credential_valid (a_domain, a_username, a_password: POINTER): BOOLEAN
cwel_is_credential_valid (a_domain, a_username, a_password: POINTER): BOOLEAN
external
external
Line 87: Line 88:
"return cwel_is_credential_valid ((LPTSTR) $a_domain, (LPTSTR) $a_username, (LPTSTR) $a_password);"
"return cwel_is_credential_valid ((LPTSTR) $a_domain, (LPTSTR) $a_username, (LPTSTR) $a_password);"
end
end
</syntaxhighlight>
</lang>

=={{header|FreeBASIC}}==
See [https://rosettacode.org/wiki/Active_Directory/Connect#FreeBASIC Active_Directory/Connect#FreeBASIC]


=={{header|Go}}==
=={{header|Go}}==
Line 93: Line 97:
<br>
<br>
There are a large number of third-party LDAP libraries for Go. This uses one of the simpler ones and the code below is largely taken from the example on its main page.
There are a large number of third-party LDAP libraries for Go. This uses one of the simpler ones and the code below is largely taken from the example on its main page.
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 117: Line 121:
}
}
log.Printf("Groups: %+v", groups)
log.Printf("Groups: %+v", groups)
}</lang>
}</syntaxhighlight>


=={{header|Haskell}}==
=={{header|Haskell}}==
Line 123: Line 127:
Example uses the [https://hackage.haskell.org/package/ldap-client <tt>ldap-client</tt>] package:
Example uses the [https://hackage.haskell.org/package/ldap-client <tt>ldap-client</tt>] package:


<lang haskell>{-# LANGUAGE OverloadedStrings #-}
<syntaxhighlight lang="haskell">{-# LANGUAGE OverloadedStrings #-}


module Main (main) where
module Main (main) where
Line 137: Line 141:
Ldap.search ldap (Ldap.Dn "o=example.com") (Ldap.typesOnly True) (Attr "uid" := Text.encodeUtf8 "user") []
Ldap.search ldap (Ldap.Dn "o=example.com") (Ldap.typesOnly True) (Attr "uid" := Text.encodeUtf8 "user") []
for_ entries $ \entry ->
for_ entries $ \entry ->
print entry</lang>
print entry</syntaxhighlight>


=={{header|Java}}==
=={{header|Java}}==
Line 143: Line 147:
The following code uses the Apache Directory project, version 1.0.0.
The following code uses the Apache Directory project, version 1.0.0.


<lang java>import java.io.IOException;
<syntaxhighlight lang="java">import java.io.IOException;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
Line 180: Line 184:
}
}
}
}
}</lang>
}</syntaxhighlight>


=={{header|Julia}}==
=={{header|Julia}}==
<lang julia>using LDAPClient
<syntaxhighlight lang="julia">using LDAPClient


function searchLDAPusers(searchstring, uname, pword, host=["example", "com"])
function searchLDAPusers(searchstring, uname, pword, host=["example", "com"])
Line 204: Line 208:


searchLDAPusers("Mario", "my-username", "my-password")
searchLDAPusers("Mario", "my-username", "my-password")
</syntaxhighlight>
</lang>


=={{header|NetRexx}}==
=={{header|NetRexx}}==
Uses the [http://directory.apache.org/api/ Apache LDAP API], connecting to a local [http://directory.apache.org/apacheds/1.5/ ApacheDS] LDAP directory server.
Uses the [http://directory.apache.org/api/ Apache LDAP API], connecting to a local [http://directory.apache.org/apacheds/1.5/ ApacheDS] LDAP directory server.
<lang NetRexx>/* NetRexx */
<syntaxhighlight lang="netrexx">/* NetRexx */
options replace format comments java crossref symbols binary
options replace format comments java crossref symbols binary


Line 327: Line 331:


return state
return state
</syntaxhighlight>
</lang>
'''Output:'''
'''Output:'''
<pre>
<pre>
Line 348: Line 352:
This program drives the <tt>ldapsearch</tt> command and captures the output into an external data queue via ooRexx <tt>rxqueue</tt> facility. The contents of the queue are then read into program variables for further processing.
This program drives the <tt>ldapsearch</tt> command and captures the output into an external data queue via ooRexx <tt>rxqueue</tt> facility. The contents of the queue are then read into program variables for further processing.


<lang ooRexx>/* Rexx */
<syntaxhighlight lang="oorexx">/* Rexx */
do
do
LDAP_URL = 'ldap://localhost:11389'
LDAP_URL = 'ldap://localhost:11389'
Line 408: Line 412:
end
end
exit
exit
</syntaxhighlight>
</lang>
'''Output:'''
'''Output:'''
<pre>
<pre>
Line 419: Line 423:
=={{header|Perl}}==
=={{header|Perl}}==
{{Trans|Raku}}
{{Trans|Raku}}
<lang perl># 20210306 Perl programming solution
<syntaxhighlight lang="perl"># 20210306 Perl programming solution


use strict;
use strict;
Line 440: Line 444:
foreach my $entry ($srch->entries) { $entry->dump }
foreach my $entry ($srch->entries) { $entry->dump }


$mesg = $ldap->unbind;</lang>
$mesg = $ldap->unbind;</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 458: Line 462:
=={{header|Phix}}==
=={{header|Phix}}==
{{trans|C}}
{{trans|C}}
<!--<lang Phix>-->
<!--<syntaxhighlight lang="phix">-->
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">/</span><span style="color: #000000;">ldap</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">/</span><span style="color: #000000;">ldap</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
Line 484: Line 488:
<span style="color: #000000;">ldap_unbind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ld</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">ldap_unbind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ld</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
Note the code inside res=LDAP_SUCCESS has not been tested beyond compiling succesfully, see also
Note the code inside res=LDAP_SUCCESS has not been tested beyond compiling succesfully, see also
Line 496: Line 500:
{{libheader|php-ldap}}
{{libheader|php-ldap}}


<lang php><?php
<syntaxhighlight lang="php"><?php


$l = ldap_connect('ldap.example.com');
$l = ldap_connect('ldap.example.com');
Line 511: Line 515:
$entries = ldap_get_entries($l, $search);
$entries = ldap_get_entries($l, $search);


var_dump($entries);</lang>
var_dump($entries);</syntaxhighlight>


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp>(de ldapsearch (Sn)
<syntaxhighlight lang="picolisp">(de ldapsearch (Sn)
(in
(in
(list "ldapsearch" "-xH" "ldap://db.debian.org"
(list "ldapsearch" "-xH" "ldap://db.debian.org"
Line 521: Line 525:
(list
(list
(cons 'cn (prog (from "cn: ") (line T)))
(cons 'cn (prog (from "cn: ") (line T)))
(cons 'uid (prog (from "uid: ") (line T))) ) ) )</lang>
(cons 'uid (prog (from "uid: ") (line T))) ) ) )</syntaxhighlight>
Test:
Test:
<pre>: (ldapsearch "Fischer")
<pre>: (ldapsearch "Fischer")
Line 528: Line 532:
=={{header|PowerShell}}==
=={{header|PowerShell}}==


<lang python>
<syntaxhighlight lang="python">
Import-Module ActiveDirectory
Import-Module ActiveDirectory


Line 537: Line 541:
get-aduser -Filter((DistinguishedName -eq $searchdata) -or (UserPrincipalName -eq $searchdata) -or (SamAccountName -eq $searchdata)) -SearchBase $searchBase
get-aduser -Filter((DistinguishedName -eq $searchdata) -or (UserPrincipalName -eq $searchdata) -or (SamAccountName -eq $searchdata)) -SearchBase $searchBase


</syntaxhighlight>
</lang>


=={{header|Python}}==
=={{header|Python}}==
Line 545: Line 549:
[http://www.python-ldap.org/doc/html/index.html python-ldap Documentation]
[http://www.python-ldap.org/doc/html/index.html python-ldap Documentation]


<lang python>import ldap
<syntaxhighlight lang="python">import ldap


l = ldap.initialize("ldap://ldap.example.com")
l = ldap.initialize("ldap://ldap.example.com")
Line 563: Line 567:
finally:
finally:
l.unbind()
l.unbind()
</syntaxhighlight>
</lang>


=={{header|Raku}}==
=={{header|Raku}}==
(formerly Perl 6)
(formerly Perl 6)
<syntaxhighlight lang="raku" line>
<lang perl6>


# 20190718 Raku programming solution
# 20190718 Raku programming solution
Line 598: Line 602:
}
}
}
}
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>objectClass -> inetOrgPerson organizationalPerson top person
<pre>objectClass -> inetOrgPerson organizationalPerson top person
Line 613: Line 617:


A little contrived; this [[REXX]] program drives the <tt>ldapsearch</tt> command.
A little contrived; this [[REXX]] program drives the <tt>ldapsearch</tt> command.
<lang REXX>/* Rexx */
<syntaxhighlight lang="rexx">/* Rexx */
do
do
LDAP_URL = 'ldap://localhost:11389'
LDAP_URL = 'ldap://localhost:11389'
Line 645: Line 649:
end
end
exit
exit
</syntaxhighlight>
</lang>
'''Output:'''
'''Output:'''
<pre>
<pre>
Line 663: Line 667:


{{libheader|RubyGems}}
{{libheader|RubyGems}}
<lang ruby>require 'rubygems'
<syntaxhighlight lang="ruby">require 'rubygems'
require 'net/ldap'
require 'net/ldap'


Line 676: Line 680:
results = ldap.search(:filter => filter) # returns an array of Net::LDAP::Entry objects
results = ldap.search(:filter => filter) # returns an array of Net::LDAP::Entry objects


puts results[0][:sn] # ==> "Jackman"</lang>
puts results[0][:sn] # ==> "Jackman"</syntaxhighlight>


=={{header|Run BASIC}}==
=={{header|Run BASIC}}==
Line 685: Line 689:
[rename] files
[rename] files
[view] image files</pre>
[view] image files</pre>
<lang runbasic>' ---------------------------------------------
<syntaxhighlight lang="runbasic">' ---------------------------------------------
' Directory maintenance
' Directory maintenance
' ---------------------------------------------
' ---------------------------------------------
Line 791: Line 795:
WEND
WEND
END FUNCTION
END FUNCTION
end</lang>
end</syntaxhighlight>
Output as seen by the client on the web<pre>
Output as seen by the client on the web<pre>
Volume in drive C has no label.
Volume in drive C has no label.
Line 813: Line 817:


=={{header|Scala}}==
=={{header|Scala}}==
<lang Scala>import org.apache.directory.api.ldap.model.message.SearchScope
<syntaxhighlight lang="scala">import org.apache.directory.api.ldap.model.message.SearchScope
import org.apache.directory.ldap.client.api.{LdapConnection, LdapNetworkConnection}
import org.apache.directory.ldap.client.api.{LdapConnection, LdapNetworkConnection}


Line 848: Line 852:
new LdapSearch().demonstrateSearch()
new LdapSearch().demonstrateSearch()


}</lang>
}</syntaxhighlight>


=={{header|Tcl}}==
=={{header|Tcl}}==
Line 854: Line 858:


This is just the basic setup.
This is just the basic setup.
<lang tcl>set Username "TestUser"
<syntaxhighlight lang="tcl">set Username "TestUser"
set Filter "((&objectClass=*)(sAMAccountName=$Username))"
set Filter "((&objectClass=*)(sAMAccountName=$Username))"
set Base "dc=skycityauckland,dc=sceg,dc=com"
set Base "dc=skycityauckland,dc=sceg,dc=com"
set Attrs distinguishedName</lang>
set Attrs distinguishedName</syntaxhighlight>


Now do the actual search.
Now do the actual search.
<lang tcl>set result [ldap::search $conn $Base $Filter $Attrs -scope subtree]</lang>
<syntaxhighlight lang="tcl">set result [ldap::search $conn $Base $Filter $Attrs -scope subtree]</syntaxhighlight>
If we have only a single result its easy:
If we have only a single result its easy:
<lang tcl>if {[llength $result] == 1} {
<syntaxhighlight lang="tcl">if {[llength $result] == 1} {
puts [dict get [lindex $result 0 1] distinguishedName]
puts [dict get [lindex $result 0 1] distinguishedName]
}</lang>
}</syntaxhighlight>


Looping over the result set to output some values.
Looping over the result set to output some values.
<lang tcl>foreach pair $result {
<syntaxhighlight lang="tcl">foreach pair $result {
lassign $pair cn attributes
lassign $pair cn attributes
puts [dict get $attributes distinguishedName]
puts [dict get $attributes distinguishedName]
}</lang>
}</syntaxhighlight>


If you're bored you can also use this instead:
If you're bored you can also use this instead:
<lang tcl>package require ldapx
<syntaxhighlight lang="tcl">package require ldapx
set conn [ldapx::connect $BindDN $Password]
set conn [ldapx::connect $BindDN $Password]
$conn traverse $Base $Filter $Attrs e {
$conn traverse $Base $Filter $Attrs e {
puts [$e get distinguishedName]
puts [$e get distinguishedName]
}</lang>
}</syntaxhighlight>


=={{header|UNIX Shell}}==
=={{header|UNIX Shell}}==
Line 884: Line 888:


A shell script to drive the <tt>ldapsearch</tt> command.
A shell script to drive the <tt>ldapsearch</tt> command.
<lang bash>#!/bin/sh
<syntaxhighlight lang="bash">#!/bin/sh


LDAP_HOST="localhost"
LDAP_HOST="localhost"
Line 908: Line 912:
$LDAP_FILTER \
$LDAP_FILTER \
$LDAP_ATTRIBUTES
$LDAP_ATTRIBUTES
</syntaxhighlight>
</lang>
'''Output:'''
'''Output:'''
<pre>
<pre>
Line 923: Line 927:
=={{header|VBScript}}==
=={{header|VBScript}}==
The search string and execution of the string
The search string and execution of the string
<lang vbscript>strUsername = "TestUser"
<syntaxhighlight lang="vbscript">strUsername = "TestUser"
strQuery = "<LDAP://dc=skycityauckland,dc=sceg,dc=com>;"_
strQuery = "<LDAP://dc=skycityauckland,dc=sceg,dc=com>;"_
& "(&(objectclass=*)(samaccountname=" & strUsername & "));distinguishedname;subtree"
& "(&(objectclass=*)(samaccountname=" & strUsername & "));distinguishedname;subtree"
Line 929: Line 933:
objCmd.Properties("Page Size")=100
objCmd.Properties("Page Size")=100
objCmd.CommandText = strQuery
objCmd.CommandText = strQuery
Set objRS = objCmd.Execute</lang>
Set objRS = objCmd.Execute</syntaxhighlight>


Doing something with a single result (this will output the returned users full DN)
Doing something with a single result (this will output the returned users full DN)
<lang vbscript>If objRS.RecordCount = 1 Then
<syntaxhighlight lang="vbscript">If objRS.RecordCount = 1 Then
WScript.Echo objRS.Fields("DistinguishedName")
WScript.Echo objRS.Fields("DistinguishedName")
End If</lang>
End If</syntaxhighlight>


Doing something with multiple results (this will output each returned users full DN)
Doing something with multiple results (this will output each returned users full DN)
<lang vbscript>If objRS.RecordCount > 0 Then
<syntaxhighlight lang="vbscript">If objRS.RecordCount > 0 Then
For Each objUser in ObjRS
For Each objUser in ObjRS
WScript.Echo objRS.Fields("DistinguishedName")
WScript.Echo objRS.Fields("DistinguishedName")
Next
Next
End If</lang>
End If</syntaxhighlight>


=={{header|Wren}}==
=={{header|Wren}}==
Line 949: Line 953:


Note that, in an actual case, one would need to wrap more LDAP functions to process the search results.
Note that, in an actual case, one would need to wrap more LDAP functions to process the search results.
<syntaxhighlight lang="wren">/* Active_Directory_Search_for_a_user.wren */
<lang ecmascript>/* active_directory_search_for_user.wren */


var LDAP_SCOPE_SUBTREE = 0x0002
var LDAP_SCOPE_SUBTREE = 0x0002
Line 999: Line 1,003:


result.msgfree()
result.msgfree()
ld.unbind()</lang>
ld.unbind()</syntaxhighlight>
<br>
<br>
We now embed this in the following C program, compile and run it.
We now embed this in the following C program, compile and run it.
<lang c>#include <stdio.h>
<syntaxhighlight lang="c">#include <stdio.h>
#include <stdio_ext.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <stdlib.h>
Line 1,129: Line 1,133:
WrenVM* vm = wrenNewVM(&config);
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* module = "main";
const char* fileName = "active_directory_search_for_user.wren";
const char* fileName = "Active_Directory_Search_for_a_user.wren";
char *script = readFile(fileName);
char *script = readFile(fileName);
WrenInterpretResult result = wrenInterpret(vm, module, script);
WrenInterpretResult result = wrenInterpret(vm, module, script);
Line 1,145: Line 1,149:
free(script);
free(script);
return 0;
return 0;
}</lang>
}</syntaxhighlight>


{{omit from|ACL2}}
{{omit from|ACL2}}
{{omit from|AWK}}
{{omit from|AWK}}
Line 1,156: Line 1,161:
{{omit from|Mathematica}}
{{omit from|Mathematica}}
{{omit from|Maxima}}
{{omit from|Maxima}}
{{omit from|MIPS Assembly|None of the commonly used implementations can access AD functions}}
{{omit from|ML/I}}
{{omit from|ML/I}}
{{omit from|MIPS Assembly|None of the commonly used implementations can access AD functions}}
{{omit from|PARI/GP}}
{{omit from|PARI/GP}}
{{omit from|PostScript}}
{{omit from|PostScript}}
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} <!-- Does not have network access. -->
{{omit from|Retro}}
{{omit from|Retro}}
{{omit from|TI-83 BASIC}}
{{omit from|TI-89 BASIC}} <!-- Does not have network access. -->
{{omit from|Yorick|Does not have network access.}}
{{omit from|Yorick|Does not have network access.}}
{{omit from|ZX Spectrum Basic|Does not have network access.}}
{{omit from|ZX Spectrum Basic|Does not have network access.}}

[[Category:Active Directory]]

Latest revision as of 21:26, 14 March 2024

Task
Active Directory/Search for a user
You are encouraged to solve this task according to the task description, using any language you may know.

Make sure you Connect to Active Directory

C

#include <ldap.h>

char *name, *password;
...

LDAP *ld = ldap_init("ldap.somewhere.com", 389);
ldap_simple_bind_s(ld, name, password);

LDAPMessage **result;
ldap_search_s(ld, "dc=somewhere,dc=com", LDAP_SCOPE_SUBTREE,
	/* search for all persons whose names start with joe or shmoe */
	"(&(objectclass=person)(|(cn=joe*)(cn=shmoe*)))",
	NULL, /* return all attributes */
	0,  /* want both types and values of attrs */
	result); /* ldap will allocate room for return messages */

/* arduously do stuff here to result, with ldap_first_message(),
	ldap_parse_result(), etc. */

ldap_msgfree(*result);	/* free messages */
ldap_unbind(ld);	/* disconnect */

D

Based on dopenldap.

import openldap;
import std.stdio;

void main() {
  // connect to server
  auto ldap = LDAP("ldap://localhost");

  // search for uid
  auto r = ldap.search_s("dc=example,dc=com", LDAP_SCOPE_SUBTREE, "(uid=%s)".format("test"));

  // show properties
  writeln("Found dn: %s", r[0].dn);
  foreach(k, v; r[0].entry)
    writeln("%s = %s", k, v);

  // bind on found entry
  int b = ldap.bind_s(r[0].dn, "password");
  scope(exit) ldap.unbind;
  if (b)
  {
    writeln("error on binding");
    return;
  }

  // do something
  ...
    
}

Eiffel

Eiffel does not have the notion of "return", but "Result". A consequence of this is that Eiffel routines are Single-entry-Single-exit, which means less bugs. In the example (below), the Result is of type BOOLEAN.

Moreover, strings in Eiffel are objects and cannot be directly passed to the Windows OS. As such, they need to undergo a format change through the facilities of a WEL_STRING, which makes the appropriate structure conversion.

feature -- Validation

	is_user_credential_valid (a_domain, a_username, a_password: READABLE_STRING_GENERAL): BOOLEAN
			-- Is the pair `a_username'/`a_password' a valid credential in `a_domain'?
		local
			l_domain, l_username, l_password: WEL_STRING
		do
			create l_domain.make (a_domain)
			create l_username.make (a_username)
			create l_password.make (a_password)
			Result := cwel_is_credential_valid (l_domain.item, l_username.item, l_password.item)
		end

Because Active Directory is a Windows OS facility, in Eiffel we must use the WEL (Windows Eiffel Library) components. Thus, the code above is not cross-platform. Moreover, the call to `cwel_is_credential_valid' is shown below:

	cwel_is_credential_valid (a_domain, a_username, a_password: POINTER): BOOLEAN
		external
			"C inline use %"wel_user_validation.h%""
		alias
			"return cwel_is_credential_valid ((LPTSTR) $a_domain, (LPTSTR) $a_username, (LPTSTR) $a_password);"
		end

FreeBASIC

See Active_Directory/Connect#FreeBASIC

Go


There are a large number of third-party LDAP libraries for Go. This uses one of the simpler ones and the code below is largely taken from the example on its main page.

package main

import (
    "log"
    "github.com/jtblin/go-ldap-client"
)

func main() {
    client := &ldap.LDAPClient{
        Base:        "dc=example,dc=com",
        Host:        "ldap.example.com",
        Port:        389,
        GroupFilter: "(memberUid=%s)",
    }
    defer client.Close()
    err := client.Connect()
    if err != nil { 
        log.Fatalf("Failed to connect : %+v", err)
    }
    groups, err := client.GetGroupsOfUser("username")
    if err != nil {
        log.Fatalf("Error getting groups for user %s: %+v", "username", err)
    }
    log.Printf("Groups: %+v", groups) 
}

Haskell

Example uses the ldap-client package:

{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import           Data.Foldable (for_)
import qualified Data.Text.Encoding as Text (encodeUtf8)
import           Ldap.Client (Attr(..), Filter(..))
import qualified Ldap.Client as Ldap (Dn(..), Host(..), search, with, typesOnly)

main :: IO ()
main = do
    entries <- Ldap.with (Ldap.Plain "localhost") 389 $ \ldap ->
        Ldap.search ldap (Ldap.Dn "o=example.com") (Ldap.typesOnly True) (Attr "uid" := Text.encodeUtf8 "user") []
    for_ entries $ \entry ->
        print entry

Java

The following code uses the Apache Directory project, version 1.0.0.

import java.io.IOException;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;

public class LdapSearchDemo {

    public static void main(String[] args) throws IOException, LdapException, CursorException {
        new LdapSearchDemo().demonstrateSearch();
    }

    private void demonstrateSearch() throws IOException, LdapException, CursorException {
        try (LdapConnection conn = new LdapNetworkConnection("localhost", 11389)) {
            conn.bind("uid=admin,ou=system", "********");
            search(conn, "*mil*");
            conn.unBind();
        }
    }

    private void search(LdapConnection connection, String uid) throws LdapException, CursorException {
        String baseDn = "ou=users,o=mojo";
        String filter = "(&(objectClass=person)(&(uid=" + uid + ")))";
        SearchScope scope = SearchScope.SUBTREE;
        String[] attributes = {"dn", "cn", "sn", "uid"};
        int ksearch = 0;

        EntryCursor cursor = connection.search(baseDn, filter, scope, attributes);
        while (cursor.next()) {
            ksearch++;
            Entry entry = cursor.get();
            System.out.printf("Search entry %d = %s%n", ksearch, entry);
        }
    }
}

Julia

using LDAPClient

function searchLDAPusers(searchstring, uname, pword, host=["example", "com"])
    conn = LDAPClient.LDAPConnection("ldap://ldap.server.net")
    LDAPClient.simple_bind(conn, uname, pword)

    search_string = "CN=Users,DC=$(host[1]),DC=$(host[2])"
    scope = LDAPClient.LDAP_SCOPE_ONELEVEL
    chain = LDAPClient.search(conn, search_string, scope, filter="(&(objectClass=person)(&(uid=$(searchstring))))")

    for entry in LDAPClient.each_entry(chain)
        println("Search for $searchstring found user $(entry["name"]) with attributes:")
        for attr in LDAPClient.each_attribute(entry)
            println("        ", attr)
        end
    end

    LDAPClient.unbind(conn)
end

searchLDAPusers("Mario", "my-username", "my-password")

NetRexx

Uses the Apache LDAP API, connecting to a local ApacheDS LDAP directory server.

/* NetRexx */
options replace format comments java crossref symbols binary

import org.apache.directory.ldap.client.api.LdapConnection
import org.apache.directory.ldap.client.api.LdapNetworkConnection
import org.apache.directory.shared.ldap.model.cursor.EntryCursor
import org.apache.directory.shared.ldap.model.entry.Entry
import org.apache.directory.shared.ldap.model.exception.LdapException
import org.apache.directory.shared.ldap.model.message.SearchScope
import org.slf4j.Logger
import org.slf4j.LoggerFactory

class RDirectorySearchLDAP public

  properties constant
    log_ = LoggerFactory.getLogger(RDirectorySearchLDAP.class)

  properties private constant
    ldapHostName = String 'localhost'
    ldapPort = int 11389
    ldapDnStr = String 'uid=admin,ou=system'
    ldapCreds = String '********'

    isTrue = boolean (1 == 1)
    isFalse = boolean (1 \== 1)

  properties private static
    connection = LdapConnection

  method main(args = String[]) public static

    connected = isFalse
    do
      connected = setUp()
      if connected then do
        search('*mil*')
        end

    finally
      if connected then do
        tearDown()
        end
    end

    return

  method search(uid = String '*') static returns boolean

    state      = isTrue
    cursor     = EntryCursor
    baseDn     = 'ou=users,o=mojo'
    filter     = '(&(objectClass=person)(&(uid=' || uid || ')))'
    scope      = SearchScope.SUBTREE
    attributes = String[] [String 'dn', 'cn', 'sn', 'uid']
    do
      if log_.isTraceEnabled() then log_.trace('LDAP search')
      if log_.isInfoEnabled() then do
        log_.info('Begin search')
        log_.info('  search base distinguished name:' baseDn)
        log_.info('  search filter:' filter)
        log_.info('  search attributes:' Arrays.asList(attributes))
        end
      cursor = connection.search(baseDn, filter, scope, attributes)
      loop ksearch = 1 while cursor.next()
        ev = cursor.get()
        if log_.isInfoEnabled() then log_.info('Search cursor entry count:' ksearch)
        if log_.isInfoEnabled() then log_.info(ev.toString())
        end ksearch
    catch lex = LdapException
      state = isFalse
      log_.error('LDAP Error in cursor loop: Iteration' ksearch, Throwable lex)
    catch ex = Exception
      state = isFalse
      log_.error('I/O Error in cursor loop: Iteration' ksearch, Throwable ex)
    end

    return state

  method setUp() static returns boolean

    state = isFalse
    do
      if log_.isInfoEnabled() then log_.info('LDAP Connection to' ldapHostName 'on port' ldapPort)
      connection = LdapNetworkConnection(ldapHostName, ldapPort)

      if log_.isTraceEnabled() then log_.trace('LDAP bind')
      connection.bind(ldapDnStr, ldapCreds)

      state = isTrue
    catch lex = LdapException
      state = isFalse
      log_.error('LDAP Error', Throwable lex)
    catch iox = IOException
      state = isFalse
      log_.error('I/O Error', Throwable iox)
    end

    return state

  method tearDown() static returns boolean

    state = isFalse
    do
      if log_.isTraceEnabled() then log_.trace('LDAP unbind')
      connection.unBind()
      state = isTrue
    catch lex = LdapException
      state = isFalse
      log_.error('LDAP Error', Throwable lex)
    finally
      do
        connection.close()
      catch iox = IOException
        state = isFalse
        log_.error('I/O Error on connection.close()', Throwable iox)
      end
    end

    return state

Output:

[16:51:37] INFO  [RDirectorySearchLDAP] - LDAP Connection to localhost on port 11389 
[16:51:39] INFO  [RDirectorySearchLDAP] - Begin search 
[16:51:39] INFO  [RDirectorySearchLDAP] -   search base distinguished name: ou=users,o=mojo 
[16:51:39] INFO  [RDirectorySearchLDAP] -   search filter: (&(objectClass=person)(&(uid=*mil*))) 
[16:51:39] INFO  [RDirectorySearchLDAP] -   search attributes: [dn, cn, sn, uid] 
[16:51:39] INFO  [RDirectorySearchLDAP] - Search cursor entry count: 1 
[16:51:39] INFO  [RDirectorySearchLDAP] - Entry 
    dn: cn=John Milton,ou=users,o=mojo 
    uid: jmilton 
    sn: Milton 
    cn: John Milton

ooRexx

Using LDAP connecting to a local ApacheDS LDAP directory server.

This program drives the ldapsearch command and captures the output into an external data queue via ooRexx rxqueue facility. The contents of the queue are then read into program variables for further processing.

/* Rexx */
do
  LDAP_URL        = 'ldap://localhost:11389'
  LDAP_DN_STR     = 'uid=admin,ou=system'
  LDAP_CREDS      = '********'
  LDAP_BASE_DN    = 'ou=users,o=mojo'
  LDAP_SCOPE      = 'sub'
  LDAP_FILTER     = '"(&(objectClass=person)(&(uid=*mil*)))"'
  LDAP_ATTRIBUTES = '"dn" "cn" "sn" "uid"'

  ldapCommand =               ,
    'ldapsearch'              ,
    '-s base'                 ,
    '-H' LDAP_URL             ,
    '-LLL'                    ,
    '-x'                      ,
    '-v'                      ,
    '-s' LDAP_SCOPE           ,
    '-D' LDAP_DN_STR          ,
    '-w' LDAP_CREDS           ,
    '-b' LDAP_BASE_DN         ,
    LDAP_FILTER               ,
    LDAP_ATTRIBUTES           ,
    '2>&1'                    ,
    '|'                       ,
    'rxqueue'                 ,
    ''

  address command,
    ldapCommand

  ldapResult. = ''
  loop ln = 1 to queued()
    parse pull line
    ldapResult.0  = ln
    ldapResult.ln = line
    end ln

  loop ln = 1 to ldapResult.0
    parse var ldapResult.ln 'dn:'  dn_   ,
      0                     'uid:' uid_  ,
      0                     'sn:'  sn_   ,
      0                     'cn:'  cn_
    select
      when length(strip(dn_,  'b')) > 0 then dn  = dn_
      when length(strip(uid_, 'b')) > 0 then uid = uid_
      when length(strip(sn_,  'b')) > 0 then sn  = sn_
      when length(strip(cn_,  'b')) > 0 then cn  = cn_
      otherwise nop
      end
    end ln

  say 'Distiguished Name:' dn
  say '      Common Name:' cn
  say '          Surname:' sn
  say '           userID:' uid

  return
end
exit

Output:

Distiguished Name:  cn=John Milton,ou=users,o=mojo
      Common Name:  John Milton
          Surname:  Milton
           userID:  jmilton

Perl

Translation of: Raku
# 20210306 Perl programming solution

use strict;
use warnings;

use Net::LDAP;

my $ldap = Net::LDAP->new( 'ldap://ldap.forumsys.com' )  or  die "$@";

my $mesg = $ldap->bind( "cn=read-only-admin,dc=example,dc=com",
                        password => "password"                  );
              
$mesg->code and die $mesg->error;

my $srch = $ldap->search( base   => "dc=example,dc=com",
                          filter => "(|(uid=gauss))"     );

$srch->code and die $srch->error;

foreach my $entry ($srch->entries) { $entry->dump }

$mesg = $ldap->unbind;
Output:
------------------------------------------------------------------------
dn:uid=gauss,dc=example,dc=com

objectClass: inetOrgPerson
             organizationalPerson
             person
             top
         cn: Carl Friedrich Gauss
         sn: Gauss
        uid: gauss
       mail: gauss@ldap.forumsys.com

Phix

Translation of: C
include builtins/ldap.e
 
constant servers = {
"ldap.somewhere.com",
}
--...
string name="name", password="passwd"
--...
for i=1 to length(servers) do
    atom ld = ldap_init(servers[i])
    integer res = ldap_simple_bind_s(ld, name, password)
    printf(1,"%s: %d [%s]\n",{servers[i],res,ldap_err_desc(res)})
    if res=LDAP_SUCCESS then
        {res, atom pMsg} = ldap_search_s(ld, "dc=somewhere,dc=com", LDAP_SCOPE_SUBTREE,
                            -- search for all persons whose names start with joe or shmoe
                            "(&(objectclass=person)(|(cn=joe*)(cn=shmoe*)))",
                            NULL, -- return all attributes
                            0)  -- want both types and values of attrs
 
        -- arduously do stuff here to result, with ldap_first_message(), ldap_parse_result(), etc.
        ldap_msgfree(pMsg)      -- free messages (embedded NULL check)
    end if
    --... after done with it...
    ldap_unbind(ld)
end for
Output:

Note the code inside res=LDAP_SUCCESS has not been tested beyond compiling succesfully, see also the comments in Active_Directory/Connect#Phix

ldap.somewhere.com: 81 [LDAP_SERVER_DOWN]

PHP

Translation of: Python
Library: php-ldap
<?php

$l = ldap_connect('ldap.example.com');
ldap_set_option($l, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($l, LDAP_OPT_REFERRALS, false);

$bind = ldap_bind($l, 'me@example.com', 'password');

$base = 'dc=example, dc=com';
$criteria = '(&(objectClass=user)(sAMAccountName=username))';
$attributes = array('displayName', 'company');

$search = ldap_search($l, $base, $criteria, $attributes);
$entries = ldap_get_entries($l, $search);

var_dump($entries);

PicoLisp

(de ldapsearch (Sn)
   (in
      (list "ldapsearch" "-xH" "ldap://db.debian.org"
         "-b" "dc=debian,dc=org"
         (pack "sn=" Sn) )
      (list
         (cons 'cn (prog (from "cn: ") (line T)))
         (cons 'uid (prog (from "uid: ") (line T))) ) ) )

Test:

: (ldapsearch "Fischer")
-> ((cn . "Mika") (uid . "mf"))

PowerShell

Import-Module ActiveDirectory

$searchData = "user name"
$searchBase = "DC=example,DC=com"

#searches by some of the most common unique identifiers
get-aduser -Filter((DistinguishedName -eq $searchdata) -or (UserPrincipalName -eq $searchdata) -or (SamAccountName -eq $searchdata)) -SearchBase $searchBase

Python

Works with: Python version 2.6
Library: python-ldap

python-ldap Documentation

import ldap

l = ldap.initialize("ldap://ldap.example.com")
try:
    l.protocol_version = ldap.VERSION3
    l.set_option(ldap.OPT_REFERRALS, 0)

    bind = l.simple_bind_s("me@example.com", "password")
    
    base = "dc=example, dc=com"
    criteria = "(&(objectClass=user)(sAMAccountName=username))"
    attributes = ['displayName', 'company']
    result = l.search_s(base, ldap.SCOPE_SUBTREE, criteria, attributes)

    results = [entry for dn, entry in result if isinstance(entry, dict)]
    print results
finally:
    l.unbind()

Raku

(formerly Perl 6)

# 20190718 Raku programming solution
# https://github.com/perl6/doc/issues/2898
# https://www.facebook.com/groups/perl6/permalink/2379873082279037/

# Reference:
# https://github.com/Altai-man/cro-ldap
# https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/

use v6.d;
use Cro::LDAP::Client;

my $client = await Cro::LDAP::Client.connect('ldap://ldap.forumsys.com');

my $bind = await $client.bind(
   name=>'cn=read-only-admin,dc=example,dc=com',password=>'password'
);
die $bind.error-message if $bind.result-code;

my $resp = $client.search(
   :dn<dc=example,dc=com>, base=>"ou=mathematicians", filter=>'(&(uid=gauss))'
);

react {
   whenever $resp -> $entry {
      for $entry.attributes.kv -> $k, $v {
         my $value-str = $v ~~ Blob ?? $v.decode !! $v.map(*.decode);
         note "$k -> $value-str";
      }
   }
}
Output:
objectClass -> inetOrgPerson organizationalPerson top person
mail -> gauss@ldap.forumsys.com
uid -> gauss
cn -> Carl Friedrich Gauss
sn -> Gauss

REXX

Works with: ooRexx
Works with: Regina

Using LDAP connecting to a local ApacheDS LDAP directory server.

A little contrived; this REXX program drives the ldapsearch command.

/* Rexx */
do
  LDAP_URL        = 'ldap://localhost:11389'
  LDAP_DN_STR     = 'uid=admin,ou=system'
  LDAP_CREDS      = '********'
  LDAP_BASE_DN    = 'ou=users,o=mojo'
  LDAP_SCOPE      = 'sub'
  LDAP_FILTER     = '"(&(objectClass=person)(&(uid=*mil*)))"'
  LDAP_ATTRIBUTES = '"dn" "cn" "sn" "uid"'

  ldapCommand =               ,
    'ldapsearch'              ,
    '-s base'                 ,
    '-H' LDAP_URL             ,
    '-LLL'                    ,
    '-x'                      ,
    '-v'                      ,
    '-s' LDAP_SCOPE           ,
    '-D' LDAP_DN_STR          ,
    '-w' LDAP_CREDS           ,
    '-b' LDAP_BASE_DN         ,
    LDAP_FILTER               ,
    LDAP_ATTRIBUTES           ,
    ''

  say ldapCommand
  address command,
    ldapCommand

  return
end
exit

Output:

ldapsearch -s base -H ldap://localhost:11389 -LLL -x -v -s sub -D uid=admin,ou=system -w ******** -b ou=users,o=mojo "(&(objectClass=person)(&(uid=*mil*)))" "dn" "cn" "sn" "uid" 
ldap_initialize( ldap://localhost:11389/??base )
filter: (&(objectClass=person)(&(uid=*mil*)))
requesting: dn cn sn uid 
dn: cn=John Milton,ou=users,o=mojo
uid: jmilton
sn: Milton
cn: John Milton

Ruby

Assume AD server talks LDAP.

Library: RubyGems
require 'rubygems'
require 'net/ldap'

ldap = Net::LDAP.new(:host => 'hostname', :base => 'base')
ldap.authenticate('bind_dn', 'bind_pass')

filter = Net::LDAP::Filter.pres('objectclass')
filter &= Net::LDAP::Filter.eq('sn','Jackman')
# or
filter = Net::LDAP::Filter.construct('(&(objectclass=*)(sn=Jackman))')

results = ldap.search(:filter => filter)  # returns an array of Net::LDAP::Entry objects

puts results[0][:sn]  # ==> "Jackman"

Run BASIC

This allows the client on the web to see their directory.
The user can click on any file or directory and this will give them the following options:
 [upload] data from their computer to the server
 [delete] data from their directory 
 [rename] files
 [view]   image files
' ---------------------------------------------
' Directory maintenance
' ---------------------------------------------
cr$	= chr$(13)
dirOf$	= "c:\*.*"		' get directory of

' -------------------------------------------
' Shell out directory
' -------------------------------------------

[dirShell]
cls
html "<table bgcolor=lightsteelblue><TR><TD id=wk></TD></TABLE>"
loc$	= strRep$(dirOf$,"*.*","")
x$	= shell$("dir ";dirOf$)

i 	= 1
while word$(x$,i,cr$) <> ""
	a$	= word$(x$,i,cr$)
	if trim$(a$)   = ""	then goto [next]
	if left$(a$,1) = " "	then goto [next]
	if left$(a$,1) = cr$	then goto [next]
	type$	= mid$(a$,26,3)
	size$	= mid$(a$,30,9)
	size$	= strRep$(size$,",","")
	size	= val(size$)
	if type$ <> "DIR" and size = 0 then goto [next]
	name$	= mid$(a$,40)
	a$	= strRep$(a$,"<","[")
	a$	= strRep$(a$,">","]")
	html left$(a$,39)
	link #ddir,name$, [doDir]
	     #ddir setkey(type$;"|";loc$;name$)
	html "<BR>"
	goto [next1]
     [next]
	print a$
     [next1]
	i = i + 1
wend
wait
[doDir]
type$	= word$(EventKey$,1,"|")
name$	= word$(EventKey$,2,"|")

if type$ = "DIR" then 
	dirOf$ = name$;"\*.*"
	goto [dirShell]
end if

html "<script> document.getElementById('wk').innerHTML = '"
nname$	= strRep$(name$,"\","\\")
html "What do you want to do with ";nname$;"<BR>"
button #dofile,	"Upload",[upload]
button #dofile,	"Delete",[delete]
button #rename, "Rename",[rename]
button #view, 	"View",	 [view]
html "';</script>"
wait

[delete] 
 kill name$
 goto [dirShell]

[view]
nname$ = strRep$(name$,"\","/")
print "File:";nname$
nname$ = mid$(nname$,3)
html "<EMBED SRC=""..";nname$;""">"
print "<EMBED SRC=""..";nname$;""">"

wait

[upload]
print "Upload File:";name$
files  #f, name$
if #f HASANSWER() = 0 then
	print "File: ";name$;" not found"
end if

' -------------------------------------
' load data to directory
' -------------------------------------
OPEN name$ FOR binary AS #f
filedata$  = input$(#f, LOF(#f))
CLOSE #f
print filedata$
wait

f$	= photoDir$;uploadId$
OPEN f$ FOR binary AS #f
PRINT  	#f, filedata$
CLOSE  	#f
wait

' --------------------------------
' string replace rep str with
' --------------------------------
FUNCTION strRep$(strRep$,rep$,with$)
ln	= len(rep$)
k	= instr(strRep$,rep$)
while k
	strRep$	= left$(strRep$,k - 1) + with$ + mid$(strRep$,k + ln)
	k	= instr(strRep$,rep$)
WEND
END FUNCTION
end

Output as seen by the client on the web

Volume in drive C has no label.
Volume Serial Number is F42C-D87A

 Directory of c:\
06/10/2009  02:42 PM                24 autoexec.bat
06/10/2009  02:42 PM                10 config.sys
01/30/2012  02:26 PM               206 csb.log
03/09/2012  10:00 AM    [DIR]          data
02/07/2012  07:48 AM       748,990,464 precise-desktop-i386.iso
03/20/2012  04:07 PM    [DIR]          Program Files
02/05/2012  05:09 PM    [DIR]          Python
03/19/2012  04:55 PM    [DIR]          rbp101
01/30/2012  02:26 PM             3,081 RHDSetup.log
01/30/2012  10:14 PM    [DIR]          Users
01/30/2012  02:35 PM    [DIR]          wamp
03/06/2012  04:00 AM    [DIR]          Windows
               5 File(s)    748,993,785 bytes
               7 Dir(s) 952,183,820,288 bytes free

Scala

import org.apache.directory.api.ldap.model.message.SearchScope
import org.apache.directory.ldap.client.api.{LdapConnection, LdapNetworkConnection}

object LdapSearchDemo extends App {

  class LdapSearch {

    def demonstrateSearch(): Unit = {

      val conn = new LdapNetworkConnection("localhost", 11389)
      try {
        conn.bind("uid=admin,ou=system", "********")
        search(conn, "*mil*")
        conn.unBind()
      } finally if (conn != null) conn.close()

    }

    private def search(connection: LdapConnection, uid: String): Unit = {
      val baseDn = "ou=users,o=mojo"
      val filter = "(&(objectClass=person)(&(uid=" + uid + ")))"
      val scope = SearchScope.SUBTREE
      val attributes = List("dn", "cn", "sn", "uid")
      var ksearch = 0
      val cursor = connection.search(baseDn, filter, scope, attributes: _*)
      while (cursor.next) {
        ksearch += 1
        val entry = cursor.get
        printf("Search entry %d = %s%n", ksearch, entry)
      }
    }
  }

  new LdapSearch().demonstrateSearch()

}

Tcl

One can do it with the low level Connect to Active Directory based handle with this code:

This is just the basic setup.

set Username "TestUser"
set Filter "((&objectClass=*)(sAMAccountName=$Username))"
set Base "dc=skycityauckland,dc=sceg,dc=com"
set Attrs distinguishedName

Now do the actual search.

set result [ldap::search $conn $Base $Filter $Attrs -scope subtree]

If we have only a single result its easy:

if {[llength $result] == 1} {
    puts [dict get [lindex $result 0 1] distinguishedName]
}

Looping over the result set to output some values.

foreach pair $result {
    lassign $pair cn attributes
    puts [dict get $attributes distinguishedName]
}

If you're bored you can also use this instead:

package require ldapx
set conn [ldapx::connect $BindDN $Password]
$conn traverse $Base $Filter $Attrs e {
    puts [$e get distinguishedName]
}

UNIX Shell

Using LDAP connecting to a local ApacheDS LDAP directory server.

A shell script to drive the ldapsearch command.

#!/bin/sh

LDAP_HOST="localhost"
LDAP_PORT=11389
LDAP_DN_STR="uid=admin,ou=system"
LDAP_CREDS="********"
LDAP_BASE_DN="ou=users,o=mojo"
LDAP_SCOPE="sub"
LDAP_FILTER="(&(objectClass=person)(&(uid=*mil*)))"
LDAP_ATTRIBUTES="dn cn sn uid"

ldapsearch \
  -s base \
  -h $LDAP_HOST \
  -p $LDAP_PORT \
  -LLL \
  -x \
  -v \
  -s $LDAP_SCOPE \
  -D $LDAP_DN_STR \
  -w $LDAP_CREDS \
  -b $LDAP_BASE_DN \
  $LDAP_FILTER \
  $LDAP_ATTRIBUTES

Output:

ldap_initialize( ldap://localhost:11389 )
filter: (&(objectClass=person)(&(uid=*mil*)))
requesting: dn cn sn uid 
dn: cn=John Milton,ou=users,o=mojo
uid: jmilton
sn: Milton
cn: John Milton

VBScript

The search string and execution of the string

strUsername = "TestUser"
strQuery = "<LDAP://dc=skycityauckland,dc=sceg,dc=com>;"_
 & "(&(objectclass=*)(samaccountname=" & strUsername & "));distinguishedname;subtree"
objCmd.ActiveConnection = objConn
objCmd.Properties("Page Size")=100
objCmd.CommandText = strQuery
Set objRS = objCmd.Execute

Doing something with a single result (this will output the returned users full DN)

If objRS.RecordCount = 1 Then
  WScript.Echo objRS.Fields("DistinguishedName")
End If

Doing something with multiple results (this will output each returned users full DN)

If objRS.RecordCount > 0 Then
  For Each objUser in ObjRS
    WScript.Echo objRS.Fields("DistinguishedName")
  Next
End If

Wren

Translation of: C
Library: OpenLDAP

As it's not currently possible for Wren-cli to access OpenLDAP directly, we embed a Wren script in a C application to complete this task.

Note that, in an actual case, one would need to wrap more LDAP functions to process the search results.

/* Active_Directory_Search_for_a_user.wren */

var LDAP_SCOPE_SUBTREE = 0x0002

foreign class LDAPMessage {
    construct new() {}

    foreign msgfree()
}

foreign class LDAP {
    construct init(host, port) {}

    foreign simpleBindS(name, password)

    foreign searchS(base, scope, filter, attrs, attrsOnly, res)

    foreign unbind()
}

class C {
    foreign static getInput(maxSize)
}

var name = ""
while (name == "") {
    System.write("Enter name : ")
    name = C.getInput(40)
}

var password = ""
while (password == "") {
    System.write("Enter password : ")
    password = C.getInput(40)
}

var ld = LDAP.init("ldap.somewhere.com", 389)
ld.simpleBindS(name, password)

var result = LDAPMessage.new()
ld.searchS(
    "dc:somewhere,dc=com",
    LDAP_SCOPE_SUBTREE,
    "(&(objectclass=person)(|(cn=joe*)(cn=shmoe*)))", // all persons whose names start with joe or shmoe
    [], 0, result
)

// do stuff with result

result.msgfree()
ld.unbind()


We now embed this in the following C program, compile and run it.

#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <ldap.h>
#include "wren.h"

/* C <=> Wren interface functions */

void C_ldapMessageAllocate(WrenVM* vm) {
    wrenSetSlotNewForeign(vm, 0, 0, sizeof(LDAPMessage*));
}

void C_msgfree(WrenVM* vm) {
    LDAPMessage* msg = *(LDAPMessage**)wrenGetSlotForeign(vm, 0);
    ldap_msgfree(msg);
}

void C_ldapAllocate(WrenVM* vm) {
    LDAP** pldap = (LDAP**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(LDAP*));
    char *host = (char *)wrenGetSlotString(vm, 1);
    int port = (int)wrenGetSlotDouble(vm, 2);
    *pldap = ldap_init(host, port);
}

void C_simpleBindS(WrenVM* vm) {
    LDAP* ldap = *(LDAP**)wrenGetSlotForeign(vm, 0);
    const char *name = wrenGetSlotString(vm, 1);
    const char *password = wrenGetSlotString(vm, 2);
    ldap_simple_bind_s(ldap, name, password);
}

void C_searchS(WrenVM* vm) {
    LDAP* ldap = *(LDAP**)wrenGetSlotForeign(vm, 0);
    const char *base = wrenGetSlotString(vm, 1);
    int scope = (int)(ber_int_t)wrenGetSlotDouble(vm, 2);
    const char *filter = wrenGetSlotString(vm, 3);
    // no need to get attrs from slot 4 as we want all of them
    int attrsonly = (int)wrenGetSlotDouble(vm, 5);
    LDAPMessage** res = (LDAPMessage**)wrenGetSlotForeign(vm, 6);
    ldap_search_s(ldap, base, scope, filter, NULL, attrsonly, res);
} 

void C_unbind(WrenVM* vm) {
    LDAP* ldap = *(LDAP**)wrenGetSlotForeign(vm, 0);
    ldap_unbind(ldap);
}

void C_getInput(WrenVM* vm) {
    int maxSize = (int)wrenGetSlotDouble(vm, 1) + 2;
    char input[maxSize];
    fgets(input, maxSize, stdin);
    __fpurge(stdin);
    input[strcspn(input, "\n")] = 0;
    wrenSetSlotString(vm, 0, (const char*)input);
}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {
    WrenForeignClassMethods methods;
    methods.finalize = NULL;
    if (strcmp(className, "LDAP") == 0) {
        methods.allocate = C_ldapAllocate;
    } else if (strcmp(className, "LDAPMessage") == 0) {
        methods.allocate = C_ldapMessageAllocate;
    }
    return methods;
}

WrenForeignMethodFn bindForeignMethod(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {
    if (strcmp(module, "main") == 0) {
        if (strcmp(className, "LDAP") == 0) {
            if (!isStatic && strcmp(signature, "simpleBindS(_,_)") == 0) return C_simpleBindS;
            if (!isStatic && strcmp(signature, "searchS(_,_,_,_,_,_)") == 0) return C_searchS;          
            if (!isStatic && strcmp(signature, "unbind()") == 0) return C_unbind;
        } else if (strcmp(className, "LDAPMessage") == 0) {
            if (!isStatic && strcmp(signature, "msgfree()") == 0) return C_msgfree;
        } else if (strcmp(className, "C") == 0) {
            if (isStatic && strcmp(signature, "getInput(_)") == 0) return C_getInput;
        }
    }
    return NULL;
}

static void writeFn(WrenVM* vm, const char* text) {
    printf("%s", text);
}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
    switch (errorType) {
        case WREN_ERROR_COMPILE:
            printf("[%s line %d] [Error] %s\n", module, line, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            printf("[%s line %d] in %s\n", module, line, msg);
            break;
        case WREN_ERROR_RUNTIME:
            printf("[Runtime Error] %s\n", msg);
            break;
    }
}

char *readFile(const char *fileName) {
    FILE *f = fopen(fileName, "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    rewind(f);
    char *script = malloc(fsize + 1);
    fread(script, 1, fsize, f);
    fclose(f);
    script[fsize] = 0;
    return script;
}

int main(int argc, char **argv) {
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = &writeFn;
    config.errorFn = &errorFn;
    config.bindForeignClassFn = &bindForeignClass;
    config.bindForeignMethodFn = &bindForeignMethod;
    WrenVM* vm = wrenNewVM(&config);
    const char* module = "main";
    const char* fileName = "Active_Directory_Search_for_a_user.wren";
    char *script = readFile(fileName);
    WrenInterpretResult result = wrenInterpret(vm, module, script);
    switch (result) {
        case WREN_RESULT_COMPILE_ERROR:
            printf("Compile Error!\n");
            break;
        case WREN_RESULT_RUNTIME_ERROR:
            printf("Runtime Error!\n");
            break;
        case WREN_RESULT_SUCCESS:
            break;
    }
    wrenFreeVM(vm);
    free(script);
    return 0;
}