'''LDAP''', an acronym for '''Lightweight Directory Access Protocol''', is a protocol for ** See Also ** [Microsoft]'s [Active Directory]: a major category of LDAP servers [http://code.activestate.com/recipes/langs/tcl/tags/ldap/%|%ActiveState Tcl Cookbook tag: ldap]: [Patrick Finnegan] provided several [IBM]-pertinent example LDAP-using scripts to the '''[Cookbook]'''. [https://groups.google.com/forum/#!topic/comp.lang.tcl/DBP8ZJX9qjQ%|%using ldap, comma in dn] ,[comp.lang.tcl] ,2009-06-22: In general, LDAP querying requires quoting of such characters as comma ** Description ** 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 ** 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: ** Servers ** [LdapTcl]: Jochen Loewer's (see below), a derivative of the latter available in [tcllib] as of 2004, ** Clients ** [Tcllib]: the '''ldap''' package provides an LDAP client. Based on Jochen Loewer's implementation. [Sensus ldap] ,by [Matt Newman]: tclLDAP: [ftp://ftp.tcl.tk/pub/tcl/all/t/tclldap/2.1/tclLDAP-2.1.tar.gz%|%tclLDAP-2.1.tar.gz] , by Tom Murray [http://web.archive.org/web/20051215095736/http://www-users.aston.ac.uk/~oweng/tclLdap-pkg/%|%tclLdap-pkg]: a repackaging by [Gareth Owen] of Tony Murray's tclLdap2.1 extension as a tcl8.x dynamic library. [ftp://ftp.tcl.tk/pub/tcl/all/l/tclLdap-pkg/1.2/tclLdap-pkg-1.2.tar.gz%|%tclLdap-pkg-1.2.tar.gz] ([http://www.transit.hanse.de/mirror/tclLdap/tclLdap-pkg-1.2.tar.gz%|%alternate]) [http://sourceforge.net/projects/legacytcl/%|%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 ** [http://web.archive.org/web/20050901144411/http://www-users.aston.ac.uk/~oweng/ldapper-1.2/%|%ldapper]: a tcl/tk LDAP search tool. [ftp://ftp.tcl.tk/pub/tcl/all/l/ldapper/1.2/ldapper-1.2.tar.gz%|%ldapper-1.2.tar.gz]([http://web.archive.org/web/20050901144411/http://www-users.aston.ac.uk/~oweng/ldapper-1.2/ldapper-1.2.tar.gz%|%alternate]) ** CGI Interface to LDAP ** [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). ** [Tcllib] lap package *** [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 ** [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: ======none $ 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* ====== <> Glossary | Internet | Protocol