Hoping somebody can assist!
We have a customer wanting Active Directory authentication of usernames. LogonUser modeled on the West Wind Logonuser routine has worked well elsewhere, but not for this customer where it invariably fails with "wrong credentials" errors. I see nothing special except that the customer has a very heavily locked down environment and a forest of over 100 AD servers.
Anyway, the customer's network guru advocates use of LDAP authentication that he says is used by everybody else, with only 1% of apps having an issue. Hence interest in getting LDAP going!
I've seen various VFP solutions using GetObject("LDAP") but those seem to traverse LDAP looking for a user match. With tens of thousands of users and a LDAP limit to number of returns, that's unlikely to be the answer.
By reviewing ldp.exe output I've found the LDAP API... but I see the same issue against LDAP sites like ldap.forumsys.com : invalid credentials.
Has anybody else got LDAP bind going with VFP? Even simple bind will do, as the customer offers ssl port 689 connection.

This is the way I have done it against a server with thousands of users
Local lcNTUserName, lcUserDomain, lcUserName, loNetwork, ccADS_NAME_INITTYPE_GC, ccADS_NAME_TYPE_NT4
Local ccADS_NAME_TYPE_1779, lcDN, loNameTrans, lNoAd
* un = Username
ccADS_NAME_INITTYPE_GC = 3
ccADS_NAME_TYPE_NT4 = 3
ccADS_NAME_TYPE_1779 = 1
lNoAd = .F.
Odse = Getobject("LDAP://RootDse")
loNetwork = Createobject("WScript.Network")
lcUserDomain = loNetwork.UserDomain
loNameTrans = Createobject("NameTranslate")
If Vartype(loNameTrans) <> "O"
return .f.
Endif
Try
loNameTrans.Init( ccADS_NAME_INITTYPE_GC, "")
loNameTrans.Set(ccADS_NAME_TYPE_NT4, lcUserDomain + "\" + un)
lcDN = loNameTrans.Get(ccADS_NAME_TYPE_1779)
loAdUser = Getobject("LDAP://" + lcDN)
Catch To oException
Endtry
If Vartype(oException) = "O"
Return .F.
Endif
oException = Null
Y=0
Do While .T.
Try
cMemberOf = loAdUser.Getex("memberOf")
If Not Empty(cMemberOf[1])
Exit
Endif
Catch To oException
Endtry
If Vartype(oException) = "O"
If oException.ErrorNo <> 0
This.seterror("AD not available")
Strtofile("Error occurred at: " + Transform(Datetime()) + CRLF +;
[ Error: ] + Str(oException.ErrorNo) + CRLF +;
[ LineNo: ] + Str(oException.Lineno) + CRLF +;
[ Message: ] + oException.Message + CRLF +;
[ Procedure: ] + oException.Procedure + CRLF +;
[ Details: ] + oException.Details + CRLF +;
[ RetryCount: ] + Str(Y) + CRLF +;
[ LineContents: ] + oException.LineContents + CRLF ;
, This.cappstartpath + "Errors.log",.T.)
lNoAd = .T.
Endif
Endif
Y=Y+1
If Y > 20
Exit
Endif
Enddo
If lNoAd
Return .F.
Endif
Local isMember
isMember = .F.
For Each odata In cMemberOf
If "Labraintranet"$odata
isMember = .T.
Exit
Endif
Endfor
If isMember
* do something
Endif
Thanks, Jukka, nice code.
Unfortunately this customer has the Windows Scripting Host locked down, and I need to authenticate username and password against their AD.
Here's what I've come up with so far:
Declare long ldap_init In Wldap32 String Hostname,long portnumber
Declare long ldap_bind_s In wldap32 Long iHandle,String cDN,string cCredential, long iMethod
DECLARE ldap_set_option IN wldap32 long iHandle,integer option,integer ivalue
Declare ldap_unbind In wldap32 Long iHandle
#Define LDAP_PORT 389
#Define LDAP_SSL_PORT 636
#Define LDAP_AUTH_SIMPLE 0x80
#Define LDAP_SUCCESS 0x00
local lcUsername,lcpassword,lcAD,liPort,liSessionHandle,liTemp
lcUsername="cn=admin,dc=demo1,dc=freeipa,dc=org"
lcpassword="Secret123"
lcad="ipa.demo1.freeipa.org" &&see https://www.freeipa.org/page/Demo
liport=389
liSessionHandle=LDAP_INIT(m.lcAD,m.liPort) &&LDAP session handle
If m.liSessionHandle=0
Error "Unable to connect to "+m.lcAD+": "+getwindowserror() &&getwindowserror function pulls latest Windows error
ELSE
ldap_set_option(m.lisessionHandle,LDAP_OPT_VERSION,3) &&version 3
liAuth=LDAP_BIND_S(m.liSessionHandle,lcUsername,lcPassword,LDAP_AUTH_SIMPLE )
ldap_unbind(m.liSessionHandle)
If m.liAuth=LDAP_SUCCESS &&user is authenticated
Else
Error "AD returned Error Code "+Transform(m.liAuth)
Endif
Endif
With supplied username and password, I'd expect authentication at this public LDAP test site... but no luck. Invariably error 49, Invalid credentials. I've experimented with LDAP_OPEN rather than LDAP_INIT and combinations with and without CN=/ UID= / DC= etc, but still Error 49, or a collection of other errors if experiments too extreme. Any advice appreciated! This API LDAP is only a few lines of code and could be awesome, if it would only work!
Turns out it's the free LDAP site at fault. Installing OpenLDAP locally has it working fine.
Also works against ldap.forumsys.com (see https://www.forumsys.com/2022/05/10/online-ldap-test-server/ ) using UID= for the various users.
lcUsername="uid=einstein,dc=example,dc=com"
lcPassword="password"
lcAD="ldap.forumsys.com"
liPort=389
Against Active Directory, there are additional options, e.g. passing an identify packet rather than simple validation- but the above code is generic, also working against openLDAP in Linux.
For completeness, herewith function to validate username/password credentials against AD or Linux LDAP like openLDAP:
function LDAP_Authenticate(lcDN,lcpassword,lcServer,liPort)
Declare long ldap_init In Wldap32 String Hostname,long portnumber
Declare long ldap_bind_s In wldap32 Long iHandle,String cDN,string cCredential, long iMethod
DECLARE ldap_set_option IN wldap32 long iHandle,integer option,integer ivalue
Declare ldap_unbind In wldap32 Long iHandle
#Define LDAP_PORT 389
#Define LDAP_SSL_PORT 636
#Define LDAP_AUTH_SIMPLE 0x80
#DEFINE LDAP_OPT_VERSION 0x11
#Define LDAP_SUCCESS 0x00
local liSessionHandle,liTemp,liAuth
*--- sample credentials for free online LDAP
lcDN="uid=einstein,dc=example,dc=com"
lcPassword="password"
lcServer="ldap.forumsys.com"
liPort=389
liSessionHandle=LDAP_INIT(m.lcServer,m.liPort) &&LDAP session handle
If m.liSessionHandle=0
Error "Unable to connect to "+m.lcServer+": "+getwindowserror() &&getwindowserror function pulls latest Windows error
ELSE
ldap_set_option(m.lisessionHandle,LDAP_OPT_VERSION,3) &&version 3
liAuth=-1
liAuth=LDAP_BIND_S(m.liSessionHandle,m.lcDN,m.lcPassword,LDAP_AUTH_SIMPLE )
ldap_unbind(m.liSessionHandle)
If m.liAuth=LDAP_SUCCESS &&user is authenticated
Else
Error "Server returned Error Code "+Transform(m.liAuth)
Endif
Endif
