go-ldap / ldap Goto Github PK
View Code? Open in Web Editor NEWBasic LDAP v3 functionality for the GO programming language.
License: Other
Basic LDAP v3 functionality for the GO programming language.
License: Other
Currently only the DelRequest
struct has a Controls field. This should be added also to AddRequest
and ModifyRequest
Would love a way to convert a DN back into the string representation. Right now, if I build a DN, there is no way to stringify it.
Example:
package main
import "github.com/go-ldap/ldap"
func main() {
ldap.CompileFilter("((cn=)")
}
When running the example, it fails to parse the filter and crashes while logging the error message:
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
panic(0x54ef60, 0xc82000a0e0)
/usr/lib/golang/src/runtime/panic.go:481 +0x3e6
github.com/go-ldap/ldap.CompileFilter(0x57d790, 0x6, 0xc820014810, 0x0, 0x0)
/root/src/github.com/go-ldap/ldap/filter.go:86 +0x41e
main.main()
/root/test/test.go:6 +0x2c
The problem is with this line in ldap/filter.go, pos gets invalid value:
return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
Hello,
Can you please tag and version this project?
I am the Debian Maintainer for go-ldap and versioning would help Debian keep up with development.
Hi,
I want a way to figure out whether an ldap connection i created has been closed or not.
I see that Conn
struct has a bool isClosing
to gain that info. But it is private.
Can you expose a public function which value of isClosing
??
Right now, Whenever i receive an error, I simply retry the search query after restarting the connection.
My use case is,
I am exposing a web service, which uses ldap. For better performance, i created the ldap connection beforehand and kept reusing it for each request.
If i had a way to know whether the connection is lost or not, it would make it simpler for me.
Thanks You
see #54
Currently it matches exactly, but with user input in mixed cases, the attributes are not found, e.g. when called with "objectclass" while "objectClass" is present, an empty list is returned.
Is the following search filter possible with this library? Didn't seem like it worked, based on my limited testing with a hacked up example_test.go.
memberOf:CN=Some-App-Group,OU=Groups,DC=example,DC=com
Also, is it possible to search nested group membership, similar to the following?
memberOf:1.2.840.113556.1.4.1941:=CN=Some-App-Group,OU=Groups,DC=example,DC=com
Apologies if this is obvious in the code -- I'm not a Go developer but need to add this functionality to an app that uses this library.
In my application i have a /etc/krb.conf
and /etc/krb5.keytab
(configured by adcli join
). It would be great if this ldap library could authenticate with this credentials as python ldap3 library.
Hi,
I am getting below error which authenticating ldap ,could some one please help me why i am this error?
ldap: finished compiling filter with extra at end: OU=User Policy 0,OU=All Users,DC=**,DC=**,DC=**,DC=com)(uniqueMember=CN=user,OU=User Policy 0,OU=All Users,DC=**,DC=**,DC=**,DC=com))
but when i trying to use ldap for user which is in different tree like below ,it working fine
dn: CN=user2,OU=Service Accounts,OU=Non-User,DC=**,DC=**,DC=**,DC=com
Does string OU=User Policy 0
is causing issue in first case?
https://github.com/go-ldap/ldap/search?utf8=%E2%9C%93&q=extra
If connection is open for a while, I try to search something again, I got error from https://github.com/go-ldap/ldap/blob/master/conn.go#L199
Should I close connection everytime when search is done?
If the handshake fails, the established connection is not closed:
err = c.Handshake()
if err != nil {
return nil, NewError(ErrorNetwork, err)
}
Hi,
I intend to use your lib for a personnal project. I would like to know if this lib is currently stable and production ready.
Thanks a lot for the amazing work anyway ! π
Having a pool of connections would be handy. My current implementation could also work with a few changes for this project. It would mean a new major version this project, as it needs the ldap.Conn as an interface:
https://github.com/zalando/go-ldap/tree/master/ldappool
Note: despite the same name, that go-ldap is a rewritten version of https://github.com/mqu/openldap, the pooling is adapted from https://github.com/fatih/pool.
(if that works, I can finally get rid of the C code :))
This was brought up in dexidp/dex#689
Currently DialTLS()
uses net.Dial
then performs an explicit handshake.[0]
We noticed this because tls.Dial
does a few other things like inferring the ServerName[1], but since the LDAP client doesn't use that code path, users have to set it explicitly.
Is there a reason this client needs to do its own handshake?
[0]
Lines 124 to 138 in d0a5ced
One critical item that is missing the ability to set a timeout during dialing. Would it be possible to have this feature added?
(moving from checklist in readme)
Hello!
The code mentions a BSD license:
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
The BSD license says:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
However, the LICENSE file was changed in 8ad9ef0
Please respect the license terms.
1.4 is almost two years old at this point, and has atomic primitives that make race avoidance easier
would do this on a release boundary (maybe v3?)
Here is an example of the LDIF I would use with the ldapadd, how do I do this in go code?
dn: cn=boobla,dc=urbn,dc=com
objectClass: top
objectClass: iNetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: boobla
sn: boobla
gn: boobla
uid: c666f5ab-1b26-4421-9eb1-50775ed94bb9
mail: [email protected]
userPassword: {crypt}boobla
Would something like this do it?
func (l *LdapInfo)AddUser() error {
req := ldap.AddRequest{
DN: "cn=boobla,dc=example,dc=org",
Attributes: []ldap.Attribute{
{
Type: "objectClass",
Vals: []string{ "top", "iNetOrgPerson" },
},
{
Type: "cn",
Vals: []string{ "boobla" },
},
{
Type: "sn",
Vals: []string{ "boobla" },
},
{
Type: "gn",
Vals: []string{ "boobla" },
},
{
Type: "uid",
Vals: []string{ "c666f5ab-1b26-4421-9eb1-50775ed96hf6" },
},
{
Type: "ou",
Vals: []string{ "Family" },
},
{
Type: "mail",
Vals: []string{ "[email protected]" },
},
{
Type: "userPassword",
Vals: []string{ "{crypt}x" },
}},
}
return l.Conn.Add(req)
}
when separating the search and bind in separate functions, a connection returned as the type *ldap.Conn cannot be used in other functions.
The search function (searchrequest2) can be called from the bindldap function which than works...but called from main...it fails...
func Bindldap(ldaphost string, port int, principal string, password string) *ldap.Conn {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldaphost, port))
if err != nil {
println("error dialing")
log.Fatal(err)
}
err = l.Bind(principal, password)
if err != nil {
println("error binding")
log.Fatal(err)
}
searchrequest2(l)
println("separator##########################################################################")
searchrequest2(l)
return l
}
func searchrequest2(l *ldap.Conn) {
searchrequest2 := ldap.NewSearchRequest(
Searchbase1, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=organizationalPerson))", // The filter to apply
Searchattributes2, // A list attributes to retrieve
nil,
)
sr, err := l.Search(searchrequest2)
if err != nil {
log.Fatal(err)
}
for _, entry := range sr.Entries {
fmt.Printf("%s: %v, %v\n", entry.DN, entry.GetAttributeValue("cn"), entry.GetAttributeValue("sAMAcountName"))
}
}
func main() {
println("bind ldap1")
A := Bindldap(Ldap1, Lport1, Prpl1, Pass1)
println("#######################################################################3seperator")
searchrequest2(A)
A.Close()
}
The third search of the same function (triggered from func main) will yield:
2017/05/22 14:44:19 LDAP Result Code 200 "": ldap: connection closed error
wich is really weird
It doesn't look like compiling a filter using extensibleMatch is working. For example: (UserAccountControl:1.2.840.113556.1.4.803:=2)
I'm using ParseDN to validate the DN format. from my understanding once ParseDN code hits ',' or '+' chars, the code assigns attribute.Value from the buffer and appends attribute to RDN.Attributes. This doesn't check whether attribute.Type is valid or not nil before appending.
The code @ https://github.com/go-ldap/ldap/blob/v2.5.0/dn.go#L147. If I enter "test,DC=example,DC=com" as DN it doesn't raise any errors even though its invalid DN input.
I have encountered an issue where a Bind Request to a particular LDAP server response with more than one response packet for the corresponding request messageID. The Bind()
method only expects a single response which it reads and then returns (running a deferred l.finishMessage(messageID)
).
In the meantime, the processMessages()
goroutine is blocking on sending this unexpected extra response packet to the result channel for the Bind request, but the Bind()
method never receives.
Going back to the deferred l.finishMessage(messageID)
: The Bind()
method wont return until this deferred call finishes but it is blocked on sending the MessageFinish
message packet to the processMessages()
goroutine which in turn is blocked because the Bind()
method never receives from its channel. This is the deadlock.
It seems that this design for the proccesMessages()
goroutine would, in general, always end up in deadlock if the server responds with more request packets than a request handler is expecting. This may be in violation of the protocol but I think that the client should be more resilient than this in handling a misbehaving server.
One solution that I've come up with is to make it so that calls to finishMessage()
also signal to the processMessages()
goroutine to stop blocking on an attempt to send a response packet for that message ID. Currently, the connection has a chanResults
map which holds results channels which correspond to specific message IDs. We could replace this with a mapping of message IDs to context objects for that message. At the very least, this context would contain 2 items:
type messageContext struct {
done chan struct{}
responses chan *PacketResponse
}
The job of finishMessage(messageID)
would change to acquiring a lock on this message context mapping, closing the done
channel, then send the MessageFinish
packet. In processMessages()
we will have to change the MessageResponse
handler to have a select
statement which attempts to either send the response packet or receive from the done
channel. If this done
channel is closed it will not block and return instantly. The value is not important, but this signals that the request handler is done and we can abandon sending response packets to it.
Now it is possible to create new ldap.Conn with NewConn instead of Dial / DialTLS, but there is no way to call .start() on the created ldap.Conn object making NewConn not much useful.
I'm new to LDAP and was trying to see if I can use the examples presented here to set something up,
The following ldapsearch command executes successfully (that is my sanity check)
ldapsearch -D "uid=tester,ou=People,dc=example,dc=com" -w "password" -p 389 -h localhost -b "dc=example,dc=com" -s sub "(objectclass=*)"
However, running the example code returns this error:
LDAP Result Code 34 "Invalid DN Syntax": invalid DN
exit status 1
For reference, here is how I modified the example before running it:
package main
import (
"crypto/tls"
"fmt"
"log"
"gopkg.in/ldap.v2"
)
func main() {
// The username and password we want to check
username := "tester"
password := "password"
bindusername := "tester"
bindpassword := "password"
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "localhost", 389))
if err != nil {
log.Fatal(err)
}
//Commenting this out as I'd like to connect without TLS
//defer l.Close()
// Reconnect with TLS
//err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
//if err != nil {
// log.Fatal(err)
//}
// First bind with a read only user
err = l.Bind(bindusername, bindpassword)
if err != nil {
log.Fatal(err)
}
// Search for the given username
searchRequest := ldap.NewSearchRequest(
"dc=example,dc=com",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=People)&(uid=%s))", username),
[]string{"dn"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Fatal(err)
}
if len(sr.Entries) != 1 {
log.Fatal("User does not exist or too many entries returned")
}
userdn := sr.Entries[0].DN
// Bind as the user to verify their password
err = l.Bind(userdn, password)
if err != nil {
log.Fatal(err)
}
// Rebind as the read only user for any futher queries
err = l.Bind(bindusername, bindpassword)
if err != nil {
log.Fatal(err)
}
}
Thanks for creating and sharing this library!
I'm reproducibly getting a nil pointer panic when StartTLS fails. I can reproduce using a free account on jumpcloud.com and trying to use StartTLS on port 686 (the TLS-enabled port). Of course, this isn't expected to work, but hopefully we can avoid a panic.
conn, err := ldap.Dial("tcp", "ldap.jumpcloud.com:686")
if err != nil {
log.Fatal("Error opening connection")
return;
}
conn.StartTLS(&tls.Config{InsecureSkipVerify: ldapConfig.ServerTLSInsecure, ServerName: serverName})
The last line there will panic and the stack trace takes us through:
https://github.com/go-ldap/ldap/blob/v2.2.1/conn.go#L182
Line 102 in 07a7330
packet
or Children
must be nil.
The panic does not occur when conn.Debug is set to true. When Debugging is enabled, we get the following output (our own logging intermingled with this package's).
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:129: (ldap) Dialing unencrypted LDAP connection: ldap.jumpcloud.com:636
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:139: (ldap) Using StartTLS on LDAP connection: ldap.jumpcloud.com:636
LDAP Request: (Universal, Constructed, Sequence and Sequence of) Len=29 "<nil>"
MessageID: (Universal, Primitive, Integer) Len=1 "1"
Start TLS: (Application, Constructed, 0x17) Len=24 "<nil>"
TLS Extended Command: (Context, Primitive, 0x00) Len=22 "1.3.6.1.4.1.1466.20037"
2016/04/11 19:18:11 flags&startTLS = 1
2016/04/11 19:18:11 1: waiting for response
2016/04/11 19:18:11 Sending message 1
2016/04/11 19:18:11 reader error: unexpected EOF
2016/04/11 19:18:11 Sending quit message and waiting for confirmation
2016/04/11 19:18:11 Shutting down - quit message received
2016/04/11 19:18:11 Closing channel for MessageID 1
2016/04/11 19:18:11 1: got response 0x0
2016/04/11 19:18:11 Closing network connection
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/client.go:143: (ldap) Failed to connect with TLS: LDAP Result Code 203 "": ldap: cannot process packet to add descriptions
2016/04/11 19:18:11 /connect/src/connect/auth/providers/ldap/ldap.go:180: (ldap) Unable to verify credentials: LDAP Result Code 203 "": ldap: cannot process packet to add descriptions
I'll have a PR opened with one solution for this shortly which will either defer a recover in the getLDAPResultCode
function or check packet.Children for nil before checking length. But open to other approaches if anyone has any ideas.
It's protocol extension is very useful for query the user with his list of security groups GUID, in a single request
More info: https://msdn.microsoft.com/en-us/library/aa366980(v=vs.85).aspx
http://ldap3.readthedocs.io/ldap3.protocol.microsoft.html#ldap3.protocol.microsoft.ExtendedDN
noirello/bonsai#6
I may be using DialTLS() wrong, but it doesn't work for me.
Consider this simple script:
package main
import (
"fmt"
"crypto/tls"
"github.com/go-ldap/ldap"
)
func main() {
conn,err := ldap.DialTLS("tcp","173.9.229.241:389",&tls.Config{InsecureSkipVerify:true})
fmt.Printf("conn: %s\nerr: %s\n", conn,err)
}
Which outputs:
Connected? false
err: LDAP Result Code 200 "": read tcp 173.9.229.241:389: connection reset by peer
However, if I change it to call StartTLS after the connection is established, it works:
package main
import (
"fmt"
"crypto/tls"
"github.com/go-ldap/ldap"
)
func main() {
conn,_ := ldap.Dial("tcp","173.9.229.241:389")
err := conn.StartTLS(&tls.Config{InsecureSkipVerify:true})
fmt.Printf("Connected? %t\nerr: %s\n", conn != nil, err)
}
Connected? true
err: %!s()
Am I using DialTLS() incorrectly?
This code almost works but can't change user password although password encoding and connection authentication works with ldmodify in command line. I can connect via port 636 and do almost all actions except unicodePwd change.
Error:
Password could not be changed: LDAP Result Code 53 "Unwilling To Perform": 0000001F: SvcErr: DSID-031A0FC0, problem 5003 (WILL_NOT_PERFORM), data 0
Possibly something having to do with TLS handshake, any ideas ?
func main() {
const rootPEM =`XXXX`
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
panic("failed to parse root certificate")
}
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", "xxxxx.domain.com", 636), &tls.Config{
RootCAs: roots,
ServerName: "xxxxx.domain.com",
InsecureSkipVerify: false,
})
if err != nil {
log.Fatal(err)
}
defer l.Close()
err = l.Bind("CN=administrator,DC=domain,DC=com", "xxxxxxxxxx")
if err != nil {
log.Fatal(err)
}
log.Println("Successfull")
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
encoded, err := utf16.NewEncoder().String("\"xxxxxxxxxxxxxx\"")
dn := "CN=testldap,DC=domain,DC=com"
fmt.Println(encoded)
str := base64.StdEncoding.EncodeToString([]byte(encoded))
fmt.Println(str)
modify := ldap.NewModifyRequest(dn)
modify.Replace("unicodePwd", []string{str})
err = l.Modify(modify)
if err != nil {
log.Fatalf("Password could not be changed: %s", err.Error())
}
}
I'm trying to add the AddRequest (https://tools.ietf.org/html/rfc4511#section-4.7) functionality to the library and got this far. My test errors out:
"LDAP Result Code 200 "": ldap: could not retrieve message"
https://gist.github.com/dreh23/d50393cb053499978594
Maybe someone can give me a hint how to get this running. TY
(moving from checklist in readme)
The following code fails with a panic:
package ldap_test
import (
"testing"
"gopkg.in/ldap.v2"
)
func TestControlPagingEncodeDecode(t *testing.T) {
c := ldap.NewControlPaging(100)
packet := c.Encode()
_ = ldap.DecodeControl(packet)
}
--- FAIL: TestControlPagingEncodeDecode (0.00s)
panic: interface conversion: interface is uint64, not int64 [recovered]
panic: interface conversion: interface is uint64, not int64
goroutine 5 [running]:
panic(0x7d5760, 0xc820056780)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
testing.tRunner.func1(0xc8200841b0)
/usr/local/go/src/testing/testing.go:467 +0x192
panic(0x7d5760, 0xc820056780)
/usr/local/go/src/runtime/panic.go:443 +0x4e9
gopkg.in/ldap%2ev2.DecodeControl(0xc820018310, 0x0, 0x0)
/home/hanno/go/src/gopkg.in/ldap.v2/control.go:283 +0x682
_/home/hanno/git/go-ldap_test.TestControlPagingEncodeDecode(0xc8200841b0)
/home/hanno/git/go-ldap/control_test.go:11 +0x5b
testing.tRunner(0xc8200841b0, 0xa3fbb8)
/usr/local/go/src/testing/testing.go:473 +0x98
created by testing.RunTests
/usr/local/go/src/testing/testing.go:582 +0x892
exit status 2
FAIL _/home/hanno/git/go-ldap 0.007s
The PagingSize value is encoded as uint64, but decoded as int64
see #62
Filter: "(&(objectclass=inetorgperson)(cn=δΈζ))"
this filter will failed
a fix similar to #83 should be applied here also (ldap.go)
func addControlDescriptions(packet *ber.Packet) {
packet.Description = "Controls"
for _, child := range packet.Children {
child.Description = "Control"
child.Children[0].Description = "Control Type (" + ControlDescription(child.Children[0].Value.(string)) + ")"
value := child.Children[1]
if len(child.Children) == 3 {
child.Children[1].Description = "Criticality"
value = child.Children[2]
}
value.Description = "Control Value"
Hello,
Sorry for opening yet another issue, but any idea on how I can handle timestamps ?
Thanks in advance π
DecodePacketErr returns error information, we should update to use this and propagate errors
also, we should add bounds checking helpers that return a child packet or an error
would do this on a major release boundary since it would probably mean tweaking function return values
When i set the password parameter as an empty string the
func (l *Conn) Bind(username, password string) error
returns nil as error so that my users can login with username and empty password.
Search request: (|(displayName=Smith)(cn=Smith))
Debug from ldap library:
Attribute: (Universal, Primitive, Octet String) Len=11 "displayName"
Substrings Any: (Context, Primitive, 0x01) Len=5 "Smith"
Attribute: (Universal, Primitive, Octet String) Len=2 "cn"
Substrings Any: (Context, Primitive, 0x01) Len=5 "Smith"
Debug by my printf in file gopkg.in/ldap.v1/filter.go, function compileFilter, line number 193:
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: * / newPos: 15
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: S / newPos: 16
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: m / newPos: 17
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: i / newPos: 18
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: t / newPos: 19
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: h / newPos: 20
Filter:(|(displayName=*Smith*)(cn=*Smith*)) / filter[newPos]: * / newPos: 21
Debug from LDAP Server:
55fbcb6d ==>backsql_search(): base="ou=sl it,ou=aup,ou=tsg,ou=quadra,o=enterprise", filter="(|(displayName=smith)(cn=smith))", scope=2, deref=0, attrsonly=0, attributes to load: custom list
55fbcb72 Constructed query: SELECT DISTINCT ldap_entries.id,ldapx_persons.id,text('inetOrgPerson') AS objectClass,ldap_entries.dn AS dn FROM ldap_entries,ldapx_persons WHERE ldapx_persons.id=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND lower(ldap_entries.dn) LIKE lower('%'||?) AND ldapx_persons.lang=0 AND ((lower(text(ldapx_persons.fullname)) LIKE '%SMITH%') OR (lower(text(ldapx_persons.surname||' '||ldapx_persons.name)) LIKE '%SMITH%'))
Debug by my printf in file gopkg.in/ldap.v1/filter.go, function compileFilter, line number 193:
Probably not a bug in the code itself, but the bind example is misleading when using Active Directory.
We are essentially using the code from https://godoc.org/gopkg.in/ldap.v2#ex-package--UserAuthentication to authenticate users. We loop through multiple LDAP and AD servers that may be used for authentication in our application. If we enter a valid username (login) but a zero length password, during the final bind operation as the user, the LDAP servers return an LDAP Result Code 53 "Unwilling To Perform" error, which is good. But on Active Directory 2012, the final user bind simply succeeds. If the wrong password is given, both the Active Directory and LDAP servers fail with a code 49 (invalid credentials), which is good. With a zero length password the AD will subsequently not let that unauthenticated but bound user see anything, but in the example as is, you have successfully authenticated so the user can then do whatever your application code allows her/him to do. We do check for zero length passwords as part of our own checks so this should never happen, but just in case we have further added a second search AS the user. That second search as the user then fails on AD with LDAP Result Code 1 "Operations Error": 000004DC: LdapErr: DSID-0C090752, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580
Just thought you should know, and maybe fix or change the example. Also here in case other folks using the library against AD get confused by this behaviour.
cheers,
Consider the SearchRequest
type:
type SearchRequest struct {
BaseDN string
Scope int
DerefAliases int
SizeLimit int
TimeLimit int
TypesOnly bool
Filter string /* <-- stringly typed */
Attributes []string
Controls []Control
}
The Filter
attribute uses "string typing". Since a filter is actually structured information (similar to an abstract syntax tree with the &
, |
, β¦ nodes), using a string here is inappropriate.
Say I want to generate filters programmatically in a secure and correct way (which happens to be the case, see hashicorp/vault#1030), then the only chance I have with the current design is
decompileFilter
, and construct a SearchRequest
object using the string that I obtained; which works but is terrible.My suggestion is to represent filters as proper structured data internally and only use the string representation on the peripherals, i.e. input and output. Here's a sketch of the data structure. (Sorry that this is in Haskell; I'm not very familiar with Go.)
data Filter
= Not Filter
| Eq Attrib Attrib
| LessEqual Attrib Attrib
β¦
parseFilter :: String -> Filter
filterAsString :: Filter -> String
Note that this may apply to other data than filters as well, maybe there are more cases of "stringly typing" in this library.
Hey,
Would it be possible to expose the isClosing method from the Connection package ?
I'm running a service and inside I use (and reuse) a connection to an LDAP but at some point the TLS connection closed and I need to know if I can reuse it.
Right now I'm checking the error string but this is not the best way to achieve it I guess.
Thanks,
Currently, errors from the LDAP server are returned untyped. It would be useful to have a separate type for each error. I'll work on this.
users are able to login if username has alphabets but If username string contains hyphen(-) then getting invalid credentials (ERROR Code 49).
Hi, thank you for the lib!
I'm trying to use it in a web server and I want to reuse the connection for the duration of the request, do you have any suggestions? Right now, I'm dialing and closing every time I want to make a query, and I make multiple queries per request.
thank you!
see #84
There's an initial implementation of persistent search (https://www.ietf.org/proceedings/50/I-D/ldapext-psearch-03.txt) in https://github.com/vetinari/ldap/tree/persistent-search
That one works e.g. with an OpenDJ (or OpenDS probably) which supports this control(s)
Code to get it running:
package main
import (
"crypto/tls"
"fmt"
"gopkg.in/ldap.v2"
)
func main() {
l, err := ldap.DialTLS("tcp", "ldap.example.org:636", &tls.Config{InsecureSkipVerify: true})
if err != nil {
panic("DialTLS: " + err.Error())
}
_, err = l.SimpleBind(ldap.NewSimpleBindRequest("uid=someone,dc=example,dc=org", "MySecret", nil))
if err != nil {
panic("SimpleBind(): " + err.Error())
}
req := &ldap.SearchRequest{
BaseDN: "ou=people,dc=example,dc=org",
Scope: ldap.ScopeWholeSubtree,
Filter: "(uid=*)",
Attributes: []string{"uid", "cn"},
}
l.Debug = true
err = l.PersistentSearch(req, []string{"any"}, true, true, callBack)
if err != nil {
panic("PersistentSearch(): " + err.Error())
}
}
func callBack(res *ldap.SearchResult) bool {
if len(res.Entries) != 0 {
entry := res.Entries[0]
fmt.Printf("%s (%s)\n", entry.GetAttributeValue("cn"), entry.GetAttributeValue("uid"))
}
if len(res.Controls) != 0 {
fmt.Printf("CTRL=%s\n", res.Controls[0].String())
}
return true
}
Note that the PersistentSearch() will never (except for errors) return.
When this is running any changes on "uid" or "cn" will cause the "callBack()" function to run.
I do not know if it's from me. Maybe it's a bug. Maybe "Basic LDAP v3 functionality" is missing coding.
When I run code from Windows 2008 Domain Controller, I get the following error. What I'm trying to do is run the modify password function
Password could not be changed: LDAP Result Code 2 "Protocol Error": 0000203D: LdapErr: DSID-0C090D9A, comment: Unknown extended request OID, data 0, v1db1 exit status 1
The ldap code I wrote with PHP v2 and v3 performs all functional operations successfully. It turns out that there is not a situation about the version from here either.
Could you tell me what the problem is ?
Do you have a plan to support LDAP Unbind?
Now following sequese dosen't exec unbind but I want to unbind between 3 and 4.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.