LDAP, an acronym for
Lightweight Directory Access Protocol, is a protocol for
See Also edit
- Microsoft's Active Directory
- a major category of LDAP servers
- ActiveState Tcl Cookbook tag: ldap
- [Patrick Finnegan] provided several IBM-pertinent example LDAP-using scripts to the Cookbook.
- using ldap, comma in dn ,comp.lang.tcl ,2009-06-22
- In general, LDAP querying requires quoting of such characters as comma
Description edit
CL observes that learning to work with LDAP can intimidate newcomers, if only for the usual complication of client-server protocols (
SNMP presents the same challenge): one must have a working server
and client before achieving the "Hello, world" level of progress. In the '90s, there were quite a few public LDAP services, and it was inviting to connect new client applications to them for quick exercise.
As far as I know, they're all gone now [task: confirm this]. Is there interest in Tclistan for a public LDAP server against which we can all practice? I might set one up ... (again, same's true for SNMP).
Tutorial Suggestions edit
Examples of the following are solicited:
- looking for the current user's LDAP information
- listing all users along with their phone numbers, etc
- adding a new user
- updating an existing user
- deleting a user
- LdapTcl
Jochen Loewer's (see below), a derivative of the latter available in
tcllib as of 2004,
- Tcllib
- the ldap package provides an LDAP client. Based on Jochen Loewer's implementation.
- Sensus ldap ,by Matt Newman
- tclLDAP
- tclLDAP-2.1.tar.gz , by Tom Murray
- tclLdap-pkg
- a repackaging by Gareth Owen of Tony Murray's tclLdap2.1 extension as a tcl8.x dynamic library. tclLdap-pkg-1.2.tar.gz (alternate)
- Legacy TCL project
- has a collection of TCL programs, among them there is a ldap client more than a decade old.
As of 2010, there is no up-to-date tcl GUI client.
Other Programs edit
- ldapper
- a tcl/tk LDAP search tool. ldapper-1.2.tar.gz(alternate)
CGI Interface to LDAP edit
musashiXXX 2010-05-10
I've written a CGI based interface to LDAP. It was designed for the sole purpose of allowing users to administer a single OU from a web interface. It's very basic at the moment but I plan on developing it further. The tarball is located here:
http://nefaria.com/scriptz/tcl-ldap-cgi.tar.gz ... there is no README or instructions (I'm working on that) but if you need any help, feel free to contact me (musashiXXX) on irc.freenode.net #tcl or via e-mail (musashi@nefaria.com).
schlenk The tcllib ldap client package was greatly enhanced in the 1.9 version of Tcllib.
- STARTTLS support (RFC 4513)
- SASL Auth support (RFC 4513)
- new ldapx subpackage to provide an OO API
- LDIF support in the ldapx package (RFC 2849)
- asynchronous operation, does no longer block your app during ldap queries
- Who am I extension supported (RFC 4532)
- New introspection commands to inspect the running connection
Using
LDIF you can for example parse Mozilla Thunderbird Addressbooks, which can be exported in ldif format, see the examples/ldap directory in the tcllib distribution.
Example: LDAP search in an MS Environment edit
[rojo
] 2011-04-26 10:20:20:
An example of an LDAP search in a MS environment:
#! /bin/sh
# \
exec tclsh "$0" ${1+"$@"}
namespace eval ldapsearch {
set settings(domain) your.domain
set settings(user) authorized_user
set settings(pass) p4sSwh1rRed
# for pw_expires, use "never" or keywords compatible with clock arithmetic
# see http://www.tcl.tk/man/tcl/TclCmd/clock.htm#M22
set settings(pw_expires) {90 days}
set settings(server_timezone) GMT
set settings(client_timezone) US/Eastern
# timeout in seconds for LDAP queries... For some reason, when searching for cn
# or displayName, results return instantly; whereas searching any other field
# (employeeID for instance) returns results in *exactly* the number of seconds
# in this timeout setting, regardless of the actual time needed to complete the
# search. Perhaps the Windows domain controller does not send its EOF-ish search
# complete signal when searching non-indexed columns the same as it does for
# sAMAccountName / cn / displayName / etc. But I digress. Set this low for
# faster queries, but not low enough that valid searches return 0 results.
set settings(timeout) 8
# Wildcard searches seem to error with ldap::secure_connect
set settings(use-ssl) false
#########################
# End of user variables #
#########################
variable settings
package require ldap
if {$settings(use-ssl)} { package require tls }
proc isid {what} {
# What does an employee ID look like in your organization?
# For this example we'll say it's in the format of E01234567
return [regexp -nocase {^E\d{8}$} $what]
}
proc search {what} {
variable settings
global env
if {$settings(use-ssl)} {
if {[catch {ldap::secure_connect $settings(domain)} idx]} { set idx [ldap::connect $settings(domain)] }
} { set idx [ldap::connect $settings(domain)] }
if {$settings(use-ssl) && [::ldap::info tls $idx]} {
puts "SSL connected to [::ldap::info ip $idx]"
} { puts "Connected to [::ldap::info ip $idx]" }
ldap::bind $idx $settings(user)@$settings(domain) $settings(pass)
set attributes {
sAMAccountName
name
displayName
employeeID
pwdLastSet
userAccountControl
memberOf
msExchHomeServerName
msExchHideFromAddressLists
}
if {[isid $what]} {
set filter "(employeeID=$what)"
set options [list -scope sub -timelimit $settings(timeout) -sizelimit 1]
} else {
set filter "|(cn=$what*)(displayName=$what*)"
set options [list -scope sub -timelimit $settings(timeout) -sizelimit 20]
}
if {[catch {
set dc "dc=[string map {. ,dc=} $settings(domain)]"
ldap::searchInit $idx $dc $filter $attributes $options
} fail]} { puts "Init failure: $fail"; ldap::unbind $idx; ldap::disconnect $idx; return }
while {![catch {ldap::searchNext $idx} flat]} {
set dn [lindex $flat 0]
set flat [lindex $flat 1]
if {![llength $flat]} { continue }
foreach attr $attributes { set res($attr) {} }
foreach {name val} $flat { set res($name) $val }
# Interpret dates from the server's time zone, displaying them in the client's
if {[info exists env(TZ)]} { set keepTZ $env(TZ) } { set keepTZ $settings(client_timezone) }
set env(TZ) $settings(server_timezone)
# $res(pwdLastSet) is measured in 100 nanosecond intervals since 1/1/1601
# convert to seconds
set res(pwdLastSet) [expr {wide($res(pwdLastSet) * pow(10,-7))}]
# convert to 1970 epoch
incr res(pwdLastSet) [clock scan {1601-1-1} -format {%Y-%m-%d}]
# expires when?
if {![regexp {\d} $settings(pw_expires)]} {
set res(pwdExpires "never"
} else {
set dur [lindex $settings(pw_expires) 0]
set unit [lindex $settings(pw_expires) 1]
set res(pwdExpires) [clock format [clock add $res(pwdLastSet) $dur $unit]\
-format {%+} -timezone $settings(client_timezone)]
}
# Set how long ago?
set res(pwdAge) "[expr {([clock seconds] - $res(pwdLastSet)) / 60 / 60 / 24}] days"
# Math finished. Make pwdLastSet human readable now.
set res(pwdLastSet) [clock format $res(pwdLastSet) -format {%+} -timezone $settings(client_timezone)]
# restore temporarily changed env(TZ)
set env(TZ) $keepTZ
# see http://support.microsoft.com/kb/305144 for $res(userAccountControl)
set UAC {
134217728 UF_USE_AES_KEYS
67108864 UF_PARTIAL_SECRETS_ACCOUNT
16777216 TRUSTED_TO_AUTH_FOR_DELEGATION
8388608 PASSWORD_EXPIRED
4194304 DONT_REQ_PREAUTH
2097152 USE_DES_KEY_ONLY
1048576 NOT_DELEGATED
524288 TRUSTED_FOR_DELEGATION
262144 SMARTCARD_REQUIRED
131072 MNS_LOGON_ACCOUNT
65536 DONT_EXPIRE_PASSWORD
8192 SERVER_TRUST_ACCOUNT
4096 WORKSTATION_TRUST_ACCOUNT
2048 INTERDOMAIN_TRUST_ACCOUNT
512 NORMAL_ACCOUNT
256 TEMP_DUPLICATE_ACCOUNT
128 ENCRYPTED_TEXT_PWD_ALLOWED
64 PASSWD_CANT_CHANGE
32 PASSWD_NOTREQD
16 LOCKOUT
8 HOMEDIR_REQUIRED
2 ACCOUNTDISABLE
1 SCRIPT
}
set flags [list]
foreach {dec flag} $UAC {
if {!$res(userAccountControl)} { break }
if {$res(userAccountControl) >= $dec} { lappend flags $flag; incr res(userAccountControl) -$dec }
}
set res(userAccountControl) $flags
set groups [list]
foreach group $res(memberOf) {
lappend groups [string map {CN= ""} [lindex [split $group ,] 0]]
}
set res(memberOf) $groups
set res(msExchHomeServerName) [lindex [split [lindex $res(msExchHomeServerName) end] =] end]
foreach {name val} [array get res] { puts "$name: $val" }
puts "\n"
}
# puts "Last value of \$flat: $flat"
ldap::searchEnd $idx
ldap::unbind $idx
ldap::disconnect $idx
return
}
}; # end namespace
if {[llength $argv]} { puts $argv; ldapsearch::search [lindex $argv 0] }
usage:
$ tclsh
% source thisfile.tcl
% ldapsearch::search employeeID
- outputs exact match where employeeID=searchterm
% ldapsearch::search username
- outputs records for (up to 20) accounts matching cn=searchterm* or displayName=searchterm*