mirror of
https://github.com/documize/community.git
synced 2025-07-19 05:09:42 +02:00
Bump version to 5.11.0
This commit is contained in:
parent
a32510b8e6
commit
510e1bd0bd
370 changed files with 18825 additions and 5454 deletions
6
vendor/github.com/go-ldap/ldap/v3/add.go
generated
vendored
6
vendor/github.com/go-ldap/ldap/v3/add.go
generated
vendored
|
@ -1,8 +1,7 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fmt"
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
|
@ -63,7 +62,6 @@ func NewAddRequest(dn string, controls []Control) *AddRequest {
|
|||
DN: dn,
|
||||
Controls: controls,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add performs the given AddRequest
|
||||
|
@ -85,7 +83,7 @@ func (l *Conn) Add(addRequest *AddRequest) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||
return fmt.Errorf("ldap: unexpected response: %d", packet.Children[1].Tag)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
209
vendor/github.com/go-ldap/ldap/v3/bind.go
generated
vendored
209
vendor/github.com/go-ldap/ldap/v3/bind.go
generated
vendored
|
@ -261,7 +261,7 @@ func parseParams(str string) (map[string]string, error) {
|
|||
var state int
|
||||
for i := 0; i <= len(str); i++ {
|
||||
switch state {
|
||||
case 0: //reading key
|
||||
case 0: // reading key
|
||||
if i == len(str) {
|
||||
return nil, fmt.Errorf("syntax error on %d", i)
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ func parseParams(str string) (map[string]string, error) {
|
|||
continue
|
||||
}
|
||||
state = 1
|
||||
case 1: //reading value
|
||||
case 1: // reading value
|
||||
if i == len(str) {
|
||||
m[key] = value
|
||||
break
|
||||
|
@ -289,7 +289,7 @@ func parseParams(str string) (map[string]string, error) {
|
|||
default:
|
||||
value += string(str[i])
|
||||
}
|
||||
case 2: //inside quotes
|
||||
case 2: // inside quotes
|
||||
if i == len(str) {
|
||||
return nil, fmt.Errorf("syntax error on %d", i)
|
||||
}
|
||||
|
@ -399,6 +399,9 @@ type NTLMBindRequest struct {
|
|||
Username string
|
||||
// Password is the credentials to bind with
|
||||
Password string
|
||||
// AllowEmptyPassword sets whether the client allows binding with an empty password
|
||||
// (normally used for unauthenticated bind).
|
||||
AllowEmptyPassword bool
|
||||
// Hash is the hex NTLM hash to bind with. Password or hash must be provided
|
||||
Hash string
|
||||
// Controls are optional controls to send with the bind request
|
||||
|
@ -442,6 +445,22 @@ func (l *Conn) NTLMBind(domain, username, password string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// NTLMUnauthenticatedBind performs an bind with an empty password.
|
||||
//
|
||||
// A username is required. The anonymous bind is not (yet) supported by the go-ntlmssp library (https://github.com/Azure/go-ntlmssp/blob/819c794454d067543bc61d29f61fef4b3c3df62c/authenticate_message.go#L87)
|
||||
//
|
||||
// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 part 3.2.5.1.2
|
||||
func (l *Conn) NTLMUnauthenticatedBind(domain, username string) error {
|
||||
req := &NTLMBindRequest{
|
||||
Domain: domain,
|
||||
Username: username,
|
||||
Password: "",
|
||||
AllowEmptyPassword: true,
|
||||
}
|
||||
_, err := l.NTLMChallengeBind(req)
|
||||
return err
|
||||
}
|
||||
|
||||
// NTLMBindWithHash performs an NTLM Bind with an NTLM hash instead of plaintext password (pass-the-hash)
|
||||
func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
|
||||
req := &NTLMBindRequest{
|
||||
|
@ -455,7 +474,7 @@ func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
|
|||
|
||||
// NTLMChallengeBind performs the NTLMSSP bind operation defined in the given request
|
||||
func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) {
|
||||
if ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
|
||||
if !ntlmBindRequest.AllowEmptyPassword && ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
|
||||
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
|
||||
}
|
||||
|
||||
|
@ -496,10 +515,11 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes
|
|||
var err error
|
||||
var responseMessage []byte
|
||||
// generate a response message to the challenge with the given Username/Password if password is provided
|
||||
if ntlmBindRequest.Password != "" {
|
||||
responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
|
||||
} else if ntlmBindRequest.Hash != "" {
|
||||
if ntlmBindRequest.Hash != "" {
|
||||
responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash)
|
||||
} else if ntlmBindRequest.Password != "" || ntlmBindRequest.AllowEmptyPassword {
|
||||
_, _, domainNeeded := ntlmssp.GetDomain(ntlmBindRequest.Username)
|
||||
responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password, domainNeeded)
|
||||
} else {
|
||||
err = fmt.Errorf("need a password or hash to generate reply")
|
||||
}
|
||||
|
@ -538,3 +558,178 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes
|
|||
err = GetLDAPError(packet)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// GSSAPIClient interface is used as the client-side implementation for the
|
||||
// GSSAPI SASL mechanism.
|
||||
// Interface inspired by GSSAPIClient from golang.org/x/crypto/ssh
|
||||
type GSSAPIClient interface {
|
||||
// InitSecContext initiates the establishment of a security context for
|
||||
// GSS-API between the client and server.
|
||||
// Initially the token parameter should be specified as nil.
|
||||
// The routine may return a outputToken which should be transferred to
|
||||
// the server, where the server will present it to AcceptSecContext.
|
||||
// If no token need be sent, InitSecContext will indicate this by setting
|
||||
// needContinue to false. To complete the context
|
||||
// establishment, one or more reply tokens may be required from the server;
|
||||
// if so, InitSecContext will return a needContinue which is true.
|
||||
// In this case, InitSecContext should be called again when the
|
||||
// reply token is received from the server, passing the reply token
|
||||
// to InitSecContext via the token parameters.
|
||||
// See RFC 4752 section 3.1.
|
||||
InitSecContext(target string, token []byte) (outputToken []byte, needContinue bool, err error)
|
||||
// NegotiateSaslAuth performs the last step of the Sasl handshake.
|
||||
// It takes a token, which, when unwrapped, describes the servers supported
|
||||
// security layers (first octet) and maximum receive buffer (remaining
|
||||
// three octets).
|
||||
// If the received token is unacceptable an error must be returned to abort
|
||||
// the handshake.
|
||||
// Outputs a signed token describing the client's selected security layer
|
||||
// and receive buffer size and optionally an authorization identity.
|
||||
// The returned token will be sent to the server and the handshake considered
|
||||
// completed successfully and the server authenticated.
|
||||
// See RFC 4752 section 3.1.
|
||||
NegotiateSaslAuth(token []byte, authzid string) ([]byte, error)
|
||||
// DeleteSecContext destroys any established secure context.
|
||||
DeleteSecContext() error
|
||||
}
|
||||
|
||||
// GSSAPIBindRequest represents a GSSAPI SASL mechanism bind request.
|
||||
// See rfc4752 and rfc4513 section 5.2.1.2.
|
||||
type GSSAPIBindRequest struct {
|
||||
// Service Principal Name user for the service ticket. Eg. "ldap/<host>"
|
||||
ServicePrincipalName string
|
||||
// (Optional) Authorization entity
|
||||
AuthZID string
|
||||
// (Optional) Controls to send with the bind request
|
||||
Controls []Control
|
||||
}
|
||||
|
||||
// GSSAPIBind performs the GSSAPI SASL bind using the provided GSSAPI client.
|
||||
func (l *Conn) GSSAPIBind(client GSSAPIClient, servicePrincipal, authzid string) error {
|
||||
return l.GSSAPIBindRequest(client, &GSSAPIBindRequest{
|
||||
ServicePrincipalName: servicePrincipal,
|
||||
AuthZID: authzid,
|
||||
})
|
||||
}
|
||||
|
||||
// GSSAPIBindRequest performs the GSSAPI SASL bind using the provided GSSAPI client.
|
||||
func (l *Conn) GSSAPIBindRequest(client GSSAPIClient, req *GSSAPIBindRequest) error {
|
||||
//nolint:errcheck
|
||||
defer client.DeleteSecContext()
|
||||
|
||||
var err error
|
||||
var reqToken []byte
|
||||
var recvToken []byte
|
||||
needInit := true
|
||||
for {
|
||||
if needInit {
|
||||
// Establish secure context between client and server.
|
||||
reqToken, needInit, err = client.InitSecContext(req.ServicePrincipalName, recvToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Secure context is set up, perform the last step of SASL handshake.
|
||||
reqToken, err = client.NegotiateSaslAuth(recvToken, req.AuthZID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Send Bind request containing the current token and extract the
|
||||
// token sent by server.
|
||||
recvToken, err = l.saslBindTokenExchange(req.Controls, reqToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !needInit && len(recvToken) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Conn) saslBindTokenExchange(reqControls []Control, reqToken []byte) ([]byte, error) {
|
||||
// Construct LDAP Bind request with GSSAPI SASL mechanism.
|
||||
envelope := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
envelope.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
|
||||
|
||||
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request")
|
||||
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version"))
|
||||
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name"))
|
||||
|
||||
auth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication")
|
||||
auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "GSSAPI", "SASL Mech"))
|
||||
if len(reqToken) > 0 {
|
||||
auth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(reqToken), "Credentials"))
|
||||
}
|
||||
request.AppendChild(auth)
|
||||
envelope.AppendChild(request)
|
||||
if len(reqControls) > 0 {
|
||||
envelope.AppendChild(encodeControls(reqControls))
|
||||
}
|
||||
|
||||
msgCtx, err := l.sendMessage(envelope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer l.finishMessage(msgCtx)
|
||||
|
||||
packet, err := l.readPacket(msgCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||
if l.Debug {
|
||||
if err = addLDAPDescriptions(packet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ber.PrintPacket(packet)
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc4511#section-4.1.1
|
||||
// packet is an envelope
|
||||
// child 0 is message id
|
||||
// child 1 is protocolOp
|
||||
if len(packet.Children) != 2 {
|
||||
return nil, fmt.Errorf("bad bind response")
|
||||
}
|
||||
|
||||
protocolOp := packet.Children[1]
|
||||
RESP:
|
||||
switch protocolOp.Description {
|
||||
case "Bind Response": // Bind Response
|
||||
// Bind Reponse is an LDAP Response (https://www.rfc-editor.org/rfc/rfc4511#section-4.1.9)
|
||||
// with an additional optional serverSaslCreds string (https://www.rfc-editor.org/rfc/rfc4511#section-4.2.2)
|
||||
// child 0 is resultCode
|
||||
resultCode := protocolOp.Children[0]
|
||||
if resultCode.Tag != ber.TagEnumerated {
|
||||
break RESP
|
||||
}
|
||||
switch resultCode.Value.(int64) {
|
||||
case 14: // Sasl bind in progress
|
||||
if len(protocolOp.Children) < 3 {
|
||||
break RESP
|
||||
}
|
||||
referral := protocolOp.Children[3]
|
||||
switch referral.Description {
|
||||
case "Referral":
|
||||
if referral.ClassType != ber.ClassContext || referral.Tag != ber.TagObjectDescriptor {
|
||||
break RESP
|
||||
}
|
||||
return ioutil.ReadAll(referral.Data)
|
||||
}
|
||||
// Optional:
|
||||
//if len(protocolOp.Children) == 4 {
|
||||
// serverSaslCreds := protocolOp.Children[4]
|
||||
//}
|
||||
case 0: // Success - Bind OK.
|
||||
// SASL layer in effect (if any) (See https://www.rfc-editor.org/rfc/rfc4513#section-5.2.1.4)
|
||||
// NOTE: SASL security layers are not supported currently.
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, GetLDAPError(packet)
|
||||
}
|
||||
|
|
11
vendor/github.com/go-ldap/ldap/v3/client.go
generated
vendored
11
vendor/github.com/go-ldap/ldap/v3/client.go
generated
vendored
|
@ -1,6 +1,7 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"time"
|
||||
)
|
||||
|
@ -9,14 +10,18 @@ import (
|
|||
type Client interface {
|
||||
Start()
|
||||
StartTLS(*tls.Config) error
|
||||
Close()
|
||||
Close() error
|
||||
GetLastError() error
|
||||
IsClosing() bool
|
||||
SetTimeout(time.Duration)
|
||||
TLSConnectionState() (tls.ConnectionState, bool)
|
||||
|
||||
Bind(username, password string) error
|
||||
UnauthenticatedBind(username string) error
|
||||
SimpleBind(*SimpleBindRequest) (*SimpleBindResult, error)
|
||||
ExternalBind() error
|
||||
NTLMUnauthenticatedBind(domain, username string) error
|
||||
Unbind() error
|
||||
|
||||
Add(*AddRequest) error
|
||||
Del(*DelRequest) error
|
||||
|
@ -28,5 +33,9 @@ type Client interface {
|
|||
PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error)
|
||||
|
||||
Search(*SearchRequest) (*SearchResult, error)
|
||||
SearchAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response
|
||||
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
|
||||
DirSync(searchRequest *SearchRequest, flags, maxAttrCount int64, cookie []byte) (*SearchResult, error)
|
||||
DirSyncAsync(ctx context.Context, searchRequest *SearchRequest, bufferSize int, flags, maxAttrCount int64, cookie []byte) Response
|
||||
Syncrepl(ctx context.Context, searchRequest *SearchRequest, bufferSize int, mode ControlSyncRequestMode, cookie []byte, reloadHint bool) Response
|
||||
}
|
||||
|
|
3
vendor/github.com/go-ldap/ldap/v3/compare.go
generated
vendored
3
vendor/github.com/go-ldap/ldap/v3/compare.go
generated
vendored
|
@ -34,7 +34,8 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) {
|
|||
msgCtx, err := l.doRequest(&CompareRequest{
|
||||
DN: dn,
|
||||
Attribute: attribute,
|
||||
Value: value})
|
||||
Value: value,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
126
vendor/github.com/go-ldap/ldap/v3/conn.go
generated
vendored
126
vendor/github.com/go-ldap/ldap/v3/conn.go
generated
vendored
|
@ -2,10 +2,10 @@ package ldap
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
@ -61,13 +61,21 @@ type messageContext struct {
|
|||
|
||||
// sendResponse should only be called within the processMessages() loop which
|
||||
// is also responsible for closing the responses channel.
|
||||
func (msgCtx *messageContext) sendResponse(packet *PacketResponse) {
|
||||
func (msgCtx *messageContext) sendResponse(packet *PacketResponse, timeout time.Duration) {
|
||||
timeoutCtx := context.Background()
|
||||
if timeout > 0 {
|
||||
var cancelFunc context.CancelFunc
|
||||
timeoutCtx, cancelFunc = context.WithTimeout(context.Background(), timeout)
|
||||
defer cancelFunc()
|
||||
}
|
||||
select {
|
||||
case msgCtx.responses <- packet:
|
||||
// Successfully sent packet to message handler.
|
||||
case <-msgCtx.done:
|
||||
// The request handler is done and will not receive more
|
||||
// packets.
|
||||
case <-timeoutCtx.Done():
|
||||
// The timeout was reached before the packet was sent.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +96,7 @@ const (
|
|||
type Conn struct {
|
||||
// requestTimeout is loaded atomically
|
||||
// so we need to ensure 64-bit alignment on 32-bit platforms.
|
||||
// https://github.com/go-ldap/ldap/pull/199
|
||||
requestTimeout int64
|
||||
conn net.Conn
|
||||
isTLS bool
|
||||
|
@ -102,6 +111,8 @@ type Conn struct {
|
|||
wgClose sync.WaitGroup
|
||||
outstandingRequests uint
|
||||
messageMutex sync.Mutex
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
var _ Client = &Conn{}
|
||||
|
@ -119,30 +130,31 @@ type DialOpt func(*DialContext)
|
|||
// DialWithDialer updates net.Dialer in DialContext.
|
||||
func DialWithDialer(d *net.Dialer) DialOpt {
|
||||
return func(dc *DialContext) {
|
||||
dc.d = d
|
||||
dc.dialer = d
|
||||
}
|
||||
}
|
||||
|
||||
// DialWithTLSConfig updates tls.Config in DialContext.
|
||||
func DialWithTLSConfig(tc *tls.Config) DialOpt {
|
||||
return func(dc *DialContext) {
|
||||
dc.tc = tc
|
||||
dc.tlsConfig = tc
|
||||
}
|
||||
}
|
||||
|
||||
// DialWithTLSDialer is a wrapper for DialWithTLSConfig with the option to
|
||||
// specify a net.Dialer to for example define a timeout or a custom resolver.
|
||||
// @deprecated Use DialWithDialer and DialWithTLSConfig instead
|
||||
func DialWithTLSDialer(tlsConfig *tls.Config, dialer *net.Dialer) DialOpt {
|
||||
return func(dc *DialContext) {
|
||||
dc.tc = tlsConfig
|
||||
dc.d = dialer
|
||||
dc.tlsConfig = tlsConfig
|
||||
dc.dialer = dialer
|
||||
}
|
||||
}
|
||||
|
||||
// DialContext contains necessary parameters to dial the given ldap URL.
|
||||
type DialContext struct {
|
||||
d *net.Dialer
|
||||
tc *tls.Config
|
||||
dialer *net.Dialer
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func (dc *DialContext) dial(u *url.URL) (net.Conn, error) {
|
||||
|
@ -150,7 +162,7 @@ func (dc *DialContext) dial(u *url.URL) (net.Conn, error) {
|
|||
if u.Path == "" || u.Path == "/" {
|
||||
u.Path = "/var/run/slapd/ldapi"
|
||||
}
|
||||
return dc.d.Dial("unix", u.Path)
|
||||
return dc.dialer.Dial("unix", u.Path)
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
|
@ -161,16 +173,21 @@ func (dc *DialContext) dial(u *url.URL) (net.Conn, error) {
|
|||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "cldap":
|
||||
if port == "" {
|
||||
port = DefaultLdapPort
|
||||
}
|
||||
return dc.dialer.Dial("udp", net.JoinHostPort(host, port))
|
||||
case "ldap":
|
||||
if port == "" {
|
||||
port = DefaultLdapPort
|
||||
}
|
||||
return dc.d.Dial("tcp", net.JoinHostPort(host, port))
|
||||
return dc.dialer.Dial("tcp", net.JoinHostPort(host, port))
|
||||
case "ldaps":
|
||||
if port == "" {
|
||||
port = DefaultLdapsPort
|
||||
}
|
||||
return tls.DialWithDialer(dc.d, "tcp", net.JoinHostPort(host, port), dc.tc)
|
||||
return tls.DialWithDialer(dc.dialer, "tcp", net.JoinHostPort(host, port), dc.tlsConfig)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unknown scheme '%s'", u.Scheme)
|
||||
|
@ -203,7 +220,8 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
|
|||
}
|
||||
|
||||
// DialURL connects to the given ldap URL.
|
||||
// The following schemas are supported: ldap://, ldaps://, ldapi://.
|
||||
// The following schemas are supported: ldap://, ldaps://, ldapi://,
|
||||
// and cldap:// (RFC1798, deprecated but used by Active Directory).
|
||||
// On success a new Conn for the connection is returned.
|
||||
func DialURL(addr string, opts ...DialOpt) (*Conn, error) {
|
||||
u, err := url.Parse(addr)
|
||||
|
@ -215,8 +233,8 @@ func DialURL(addr string, opts ...DialOpt) (*Conn, error) {
|
|||
for _, opt := range opts {
|
||||
opt(&dc)
|
||||
}
|
||||
if dc.d == nil {
|
||||
dc.d = &net.Dialer{Timeout: DefaultTimeout}
|
||||
if dc.dialer == nil {
|
||||
dc.dialer = &net.Dialer{Timeout: DefaultTimeout}
|
||||
}
|
||||
|
||||
c, err := dc.dial(u)
|
||||
|
@ -231,7 +249,7 @@ func DialURL(addr string, opts ...DialOpt) (*Conn, error) {
|
|||
|
||||
// NewConn returns a new Conn using conn for network I/O.
|
||||
func NewConn(conn net.Conn, isTLS bool) *Conn {
|
||||
return &Conn{
|
||||
l := &Conn{
|
||||
conn: conn,
|
||||
chanConfirm: make(chan struct{}),
|
||||
chanMessageID: make(chan int64),
|
||||
|
@ -240,11 +258,12 @@ func NewConn(conn net.Conn, isTLS bool) *Conn {
|
|||
requestTimeout: 0,
|
||||
isTLS: isTLS,
|
||||
}
|
||||
l.wgClose.Add(1)
|
||||
return l
|
||||
}
|
||||
|
||||
// Start initializes goroutines to read responses and process messages
|
||||
func (l *Conn) Start() {
|
||||
l.wgClose.Add(1)
|
||||
go l.reader()
|
||||
go l.processMessages()
|
||||
}
|
||||
|
@ -260,31 +279,45 @@ func (l *Conn) setClosing() bool {
|
|||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (l *Conn) Close() {
|
||||
func (l *Conn) Close() (err error) {
|
||||
l.messageMutex.Lock()
|
||||
defer l.messageMutex.Unlock()
|
||||
|
||||
if l.setClosing() {
|
||||
l.Debug.Printf("Sending quit message and waiting for confirmation")
|
||||
l.chanMessage <- &messagePacket{Op: MessageQuit}
|
||||
<-l.chanConfirm
|
||||
|
||||
timeoutCtx := context.Background()
|
||||
if l.getTimeout() > 0 {
|
||||
var cancelFunc context.CancelFunc
|
||||
timeoutCtx, cancelFunc = context.WithTimeout(timeoutCtx, time.Duration(l.getTimeout()))
|
||||
defer cancelFunc()
|
||||
}
|
||||
select {
|
||||
case <-l.chanConfirm:
|
||||
// Confirmation was received.
|
||||
case <-timeoutCtx.Done():
|
||||
// The timeout was reached before confirmation was received.
|
||||
}
|
||||
|
||||
close(l.chanMessage)
|
||||
|
||||
l.Debug.Printf("Closing network connection")
|
||||
if err := l.conn.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = l.conn.Close()
|
||||
l.wgClose.Done()
|
||||
}
|
||||
l.wgClose.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
|
||||
func (l *Conn) SetTimeout(timeout time.Duration) {
|
||||
if timeout > 0 {
|
||||
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
|
||||
}
|
||||
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
|
||||
}
|
||||
|
||||
func (l *Conn) getTimeout() int64 {
|
||||
return atomic.LoadInt64(&l.requestTimeout)
|
||||
}
|
||||
|
||||
// Returns the next available messageID
|
||||
|
@ -295,6 +328,14 @@ func (l *Conn) nextMessageID() int64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
// GetLastError returns the last recorded error from goroutines like processMessages and reader.
|
||||
// Only the last recorded error will be returned.
|
||||
func (l *Conn) GetLastError() error {
|
||||
l.messageMutex.Lock()
|
||||
defer l.messageMutex.Unlock()
|
||||
return l.err
|
||||
}
|
||||
|
||||
// StartTLS sends the command to start a TLS session and then creates a new TLS Client
|
||||
func (l *Conn) StartTLS(config *tls.Config) error {
|
||||
if l.isTLS {
|
||||
|
@ -443,13 +484,13 @@ func (l *Conn) sendProcessMessage(message *messagePacket) bool {
|
|||
func (l *Conn) processMessages() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("ldap: recovered panic in processMessages: %v", err)
|
||||
l.err = fmt.Errorf("ldap: recovered panic in processMessages: %v", err)
|
||||
}
|
||||
for messageID, msgCtx := range l.messageContexts {
|
||||
// If we are closing due to an error, inform anyone who
|
||||
// is waiting about the error.
|
||||
if l.IsClosing() && l.closeErr.Load() != nil {
|
||||
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
|
||||
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)}, time.Duration(l.getTimeout()))
|
||||
}
|
||||
l.Debug.Printf("Closing channel for MessageID %d", messageID)
|
||||
close(msgCtx.responses)
|
||||
|
@ -477,7 +518,7 @@ func (l *Conn) processMessages() {
|
|||
_, err := l.conn.Write(buf)
|
||||
if err != nil {
|
||||
l.Debug.Printf("Error Sending Message: %s", err.Error())
|
||||
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)})
|
||||
message.Context.sendResponse(&PacketResponse{Error: fmt.Errorf("unable to send request: %s", err)}, time.Duration(l.getTimeout()))
|
||||
close(message.Context.responses)
|
||||
break
|
||||
}
|
||||
|
@ -487,28 +528,35 @@ func (l *Conn) processMessages() {
|
|||
l.messageContexts[message.MessageID] = message.Context
|
||||
|
||||
// Add timeout if defined
|
||||
requestTimeout := time.Duration(atomic.LoadInt64(&l.requestTimeout))
|
||||
requestTimeout := l.getTimeout()
|
||||
if requestTimeout > 0 {
|
||||
go func() {
|
||||
timer := time.NewTimer(time.Duration(requestTimeout))
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
|
||||
l.err = fmt.Errorf("ldap: recovered panic in RequestTimeout: %v", err)
|
||||
}
|
||||
|
||||
timer.Stop()
|
||||
}()
|
||||
time.Sleep(requestTimeout)
|
||||
timeoutMessage := &messagePacket{
|
||||
Op: MessageTimeout,
|
||||
MessageID: message.MessageID,
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
timeoutMessage := &messagePacket{
|
||||
Op: MessageTimeout,
|
||||
MessageID: message.MessageID,
|
||||
}
|
||||
l.sendProcessMessage(timeoutMessage)
|
||||
case <-message.Context.done:
|
||||
}
|
||||
l.sendProcessMessage(timeoutMessage)
|
||||
}()
|
||||
}
|
||||
case MessageResponse:
|
||||
l.Debug.Printf("Receiving message %d", message.MessageID)
|
||||
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
|
||||
msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
|
||||
msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.getTimeout()))
|
||||
} else {
|
||||
log.Printf("Received unexpected message %d, %v", message.MessageID, l.IsClosing())
|
||||
l.err = fmt.Errorf("ldap: received unexpected message %d, %v", message.MessageID, l.IsClosing())
|
||||
l.Debug.PrintPacket(message.Packet)
|
||||
}
|
||||
case MessageTimeout:
|
||||
|
@ -516,7 +564,7 @@ func (l *Conn) processMessages() {
|
|||
// All reads will return immediately
|
||||
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
|
||||
l.Debug.Printf("Receiving message timeout for %d", message.MessageID)
|
||||
msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))})
|
||||
msgCtx.sendResponse(&PacketResponse{message.Packet, NewError(ErrorNetwork, errors.New("ldap: connection timed out"))}, time.Duration(l.getTimeout()))
|
||||
delete(l.messageContexts, message.MessageID)
|
||||
close(msgCtx.responses)
|
||||
}
|
||||
|
@ -535,7 +583,7 @@ func (l *Conn) reader() {
|
|||
cleanstop := false
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("ldap: recovered panic in reader: %v", err)
|
||||
l.err = fmt.Errorf("ldap: recovered panic in reader: %v", err)
|
||||
}
|
||||
if !cleanstop {
|
||||
l.Close()
|
||||
|
|
788
vendor/github.com/go-ldap/ldap/v3/control.go
generated
vendored
788
vendor/github.com/go-ldap/ldap/v3/control.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -20,6 +21,13 @@ const (
|
|||
ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
|
||||
// ControlTypeWhoAmI - https://tools.ietf.org/html/rfc4532
|
||||
ControlTypeWhoAmI = "1.3.6.1.4.1.4203.1.11.3"
|
||||
// ControlTypeSubtreeDelete - https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
|
||||
ControlTypeSubtreeDelete = "1.2.840.113556.1.4.805"
|
||||
|
||||
// ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
|
||||
ControlTypeServerSideSorting = "1.2.840.113556.1.4.473"
|
||||
// ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
|
||||
ControlTypeServerSideSortingResult = "1.2.840.113556.1.4.474"
|
||||
|
||||
// ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
|
||||
ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
|
||||
|
@ -27,16 +35,43 @@ const (
|
|||
ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417"
|
||||
// ControlTypeMicrosoftServerLinkTTL - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN
|
||||
ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309"
|
||||
// ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
|
||||
ControlTypeDirSync = "1.2.840.113556.1.4.841"
|
||||
|
||||
// ControlTypeSyncRequest - https://www.ietf.org/rfc/rfc4533.txt
|
||||
ControlTypeSyncRequest = "1.3.6.1.4.1.4203.1.9.1.1"
|
||||
// ControlTypeSyncState - https://www.ietf.org/rfc/rfc4533.txt
|
||||
ControlTypeSyncState = "1.3.6.1.4.1.4203.1.9.1.2"
|
||||
// ControlTypeSyncDone - https://www.ietf.org/rfc/rfc4533.txt
|
||||
ControlTypeSyncDone = "1.3.6.1.4.1.4203.1.9.1.3"
|
||||
// ControlTypeSyncInfo - https://www.ietf.org/rfc/rfc4533.txt
|
||||
ControlTypeSyncInfo = "1.3.6.1.4.1.4203.1.9.1.4"
|
||||
)
|
||||
|
||||
// Flags for DirSync control
|
||||
const (
|
||||
DirSyncIncrementalValues int64 = 2147483648
|
||||
DirSyncPublicDataOnly int64 = 8192
|
||||
DirSyncAncestorsFirstOrder int64 = 2048
|
||||
DirSyncObjectSecurity int64 = 1
|
||||
)
|
||||
|
||||
// ControlTypeMap maps controls to text descriptions
|
||||
var ControlTypeMap = map[string]string{
|
||||
ControlTypePaging: "Paging",
|
||||
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
||||
ControlTypeManageDsaIT: "Manage DSA IT",
|
||||
ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
|
||||
ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
|
||||
ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft",
|
||||
ControlTypePaging: "Paging",
|
||||
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
||||
ControlTypeManageDsaIT: "Manage DSA IT",
|
||||
ControlTypeSubtreeDelete: "Subtree Delete Control",
|
||||
ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
|
||||
ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
|
||||
ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft",
|
||||
ControlTypeServerSideSorting: "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
|
||||
ControlTypeServerSideSortingResult: "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
|
||||
ControlTypeDirSync: "DirSync",
|
||||
ControlTypeSyncRequest: "Sync Request",
|
||||
ControlTypeSyncState: "Sync State",
|
||||
ControlTypeSyncDone: "Sync Done",
|
||||
ControlTypeSyncInfo: "Sync Info",
|
||||
}
|
||||
|
||||
// Control defines an interface controls provide to encode and describe themselves
|
||||
|
@ -229,7 +264,7 @@ func (c *ControlManageDsaIT) GetControlType() string {
|
|||
|
||||
// Encode returns the ber packet representation
|
||||
func (c *ControlManageDsaIT) Encode() *ber.Packet {
|
||||
//FIXME
|
||||
// FIXME
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
|
||||
if c.Criticality {
|
||||
|
@ -369,7 +404,13 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
|
|||
|
||||
case 2:
|
||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
||||
ControlType = packet.Children[0].Value.(string)
|
||||
if packet.Children[0].Value != nil {
|
||||
ControlType = packet.Children[0].Value.(string)
|
||||
} else if packet.Children[0].Data != nil {
|
||||
ControlType = packet.Children[0].Data.String()
|
||||
} else {
|
||||
return nil, fmt.Errorf("not found where to get the control type")
|
||||
}
|
||||
|
||||
// Children[1] could be criticality or value (both are optional)
|
||||
// duck-type on whether this is a boolean
|
||||
|
@ -436,18 +477,18 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
|
|||
|
||||
for _, child := range sequence.Children {
|
||||
if child.Tag == 0 {
|
||||
//Warning
|
||||
// Warning
|
||||
warningPacket := child.Children[0]
|
||||
val, err := ber.ParseInt64(warningPacket.Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
if warningPacket.Tag == 0 {
|
||||
//timeBeforeExpiration
|
||||
// timeBeforeExpiration
|
||||
c.Expire = val
|
||||
warningPacket.Value = c.Expire
|
||||
} else if warningPacket.Tag == 1 {
|
||||
//graceAuthNsRemaining
|
||||
// graceAuthNsRemaining
|
||||
c.Grace = val
|
||||
warningPacket.Value = c.Grace
|
||||
}
|
||||
|
@ -485,6 +526,36 @@ func DecodeControl(packet *ber.Packet) (Control, error) {
|
|||
return NewControlMicrosoftShowDeleted(), nil
|
||||
case ControlTypeMicrosoftServerLinkTTL:
|
||||
return NewControlMicrosoftServerLinkTTL(), nil
|
||||
case ControlTypeSubtreeDelete:
|
||||
return NewControlSubtreeDelete(), nil
|
||||
case ControlTypeServerSideSorting:
|
||||
return NewControlServerSideSorting(value)
|
||||
case ControlTypeServerSideSortingResult:
|
||||
return NewControlServerSideSortingResult(value)
|
||||
case ControlTypeDirSync:
|
||||
value.Description += " (DirSync)"
|
||||
return NewResponseControlDirSync(value)
|
||||
case ControlTypeSyncState:
|
||||
value.Description += " (Sync State)"
|
||||
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
return NewControlSyncState(valueChildren)
|
||||
case ControlTypeSyncDone:
|
||||
value.Description += " (Sync Done)"
|
||||
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
return NewControlSyncDone(valueChildren)
|
||||
case ControlTypeSyncInfo:
|
||||
value.Description += " (Sync Info)"
|
||||
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
return NewControlSyncInfo(valueChildren)
|
||||
default:
|
||||
c := new(ControlString)
|
||||
c.ControlType = ControlType
|
||||
|
@ -519,6 +590,35 @@ func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
|
|||
}
|
||||
}
|
||||
|
||||
// ControlSubtreeDelete implements the subtree delete control described in
|
||||
// https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
|
||||
type ControlSubtreeDelete struct{}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlSubtreeDelete) GetControlType() string {
|
||||
return ControlTypeSubtreeDelete
|
||||
}
|
||||
|
||||
// NewControlSubtreeDelete returns a ControlSubtreeDelete control.
|
||||
func NewControlSubtreeDelete() *ControlSubtreeDelete {
|
||||
return &ControlSubtreeDelete{}
|
||||
}
|
||||
|
||||
// Encode returns the ber packet representation
|
||||
func (c *ControlSubtreeDelete) Encode() *ber.Packet {
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSubtreeDelete, "Control Type ("+ControlTypeMap[ControlTypeSubtreeDelete]+")"))
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func (c *ControlSubtreeDelete) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q)",
|
||||
ControlTypeMap[ControlTypeSubtreeDelete],
|
||||
ControlTypeSubtreeDelete)
|
||||
}
|
||||
|
||||
func encodeControls(controls []Control) *ber.Packet {
|
||||
packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
|
||||
for _, control := range controls {
|
||||
|
@ -526,3 +626,669 @@ func encodeControls(controls []Control) *ber.Packet {
|
|||
}
|
||||
return packet
|
||||
}
|
||||
|
||||
// ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
|
||||
type ControlDirSync struct {
|
||||
Criticality bool
|
||||
Flags int64
|
||||
MaxAttrCount int64
|
||||
Cookie []byte
|
||||
}
|
||||
|
||||
// @deprecated Use NewRequestControlDirSync instead
|
||||
func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync {
|
||||
return NewRequestControlDirSync(flags, maxAttrCount, cookie)
|
||||
}
|
||||
|
||||
// NewRequestControlDirSync returns a dir sync control
|
||||
func NewRequestControlDirSync(
|
||||
flags int64, maxAttrCount int64, cookie []byte,
|
||||
) *ControlDirSync {
|
||||
return &ControlDirSync{
|
||||
Criticality: true,
|
||||
Flags: flags,
|
||||
MaxAttrCount: maxAttrCount,
|
||||
Cookie: cookie,
|
||||
}
|
||||
}
|
||||
|
||||
// NewResponseControlDirSync returns a dir sync control
|
||||
func NewResponseControlDirSync(value *ber.Packet) (*ControlDirSync, error) {
|
||||
if value.Value != nil {
|
||||
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
value.Data.Truncate(0)
|
||||
value.Value = nil
|
||||
value.AppendChild(valueChildren)
|
||||
}
|
||||
child := value.Children[0]
|
||||
if len(child.Children) != 3 { // also on initial creation, Cookie is an empty string
|
||||
return nil, fmt.Errorf("invalid number of children in dirSync control")
|
||||
}
|
||||
child.Description = "DirSync Control Value"
|
||||
child.Children[0].Description = "Flags"
|
||||
child.Children[1].Description = "MaxAttrCount"
|
||||
child.Children[2].Description = "Cookie"
|
||||
|
||||
cookie := child.Children[2].Data.Bytes()
|
||||
child.Children[2].Value = cookie
|
||||
return &ControlDirSync{
|
||||
Criticality: true,
|
||||
Flags: child.Children[0].Value.(int64),
|
||||
MaxAttrCount: child.Children[1].Value.(int64),
|
||||
Cookie: cookie,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlDirSync) GetControlType() string {
|
||||
return ControlTypeDirSync
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlDirSync) String() string {
|
||||
return fmt.Sprintf(
|
||||
"ControlType: %s (%q) Criticality: %t ControlValue: Flags: %d MaxAttrCount: %d",
|
||||
ControlTypeMap[ControlTypeDirSync],
|
||||
ControlTypeDirSync,
|
||||
c.Criticality,
|
||||
c.Flags,
|
||||
c.MaxAttrCount,
|
||||
)
|
||||
}
|
||||
|
||||
// Encode returns the ber packet representation
|
||||
func (c *ControlDirSync) Encode() *ber.Packet {
|
||||
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie")
|
||||
if len(c.Cookie) != 0 {
|
||||
cookie.Value = c.Cookie
|
||||
cookie.Data.Write(c.Cookie)
|
||||
}
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")"))
|
||||
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always
|
||||
|
||||
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)")
|
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value")
|
||||
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags"))
|
||||
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCount), "MaxAttrCount"))
|
||||
seq.AppendChild(cookie)
|
||||
val.AppendChild(seq)
|
||||
|
||||
packet.AppendChild(val)
|
||||
return packet
|
||||
}
|
||||
|
||||
// SetCookie stores the given cookie in the dirSync control
|
||||
func (c *ControlDirSync) SetCookie(cookie []byte) {
|
||||
c.Cookie = cookie
|
||||
}
|
||||
|
||||
// ControlServerSideSorting
|
||||
|
||||
type SortKey struct {
|
||||
Reverse bool
|
||||
AttributeType string
|
||||
MatchingRule string
|
||||
}
|
||||
|
||||
type ControlServerSideSorting struct {
|
||||
SortKeys []*SortKey
|
||||
}
|
||||
|
||||
func (c *ControlServerSideSorting) GetControlType() string {
|
||||
return ControlTypeServerSideSorting
|
||||
}
|
||||
|
||||
func NewControlServerSideSorting(value *ber.Packet) (*ControlServerSideSorting, error) {
|
||||
sortKeys := []*SortKey{}
|
||||
|
||||
val := value.Children[1].Children
|
||||
|
||||
if len(val) != 1 {
|
||||
return nil, fmt.Errorf("no sequence value in packet")
|
||||
}
|
||||
|
||||
sequences := val[0].Children
|
||||
|
||||
for i, sequence := range sequences {
|
||||
sortKey := &SortKey{}
|
||||
|
||||
if len(sequence.Children) < 2 {
|
||||
return nil, fmt.Errorf("attributeType or matchingRule is missing from sequence %d", i)
|
||||
}
|
||||
|
||||
sortKey.AttributeType = sequence.Children[0].Value.(string)
|
||||
sortKey.MatchingRule = sequence.Children[1].Value.(string)
|
||||
|
||||
if len(sequence.Children) == 3 {
|
||||
sortKey.Reverse = sequence.Children[2].Value.(bool)
|
||||
}
|
||||
|
||||
sortKeys = append(sortKeys, sortKey)
|
||||
}
|
||||
|
||||
return &ControlServerSideSorting{SortKeys: sortKeys}, nil
|
||||
}
|
||||
|
||||
func NewControlServerSideSortingWithSortKeys(sortKeys []*SortKey) *ControlServerSideSorting {
|
||||
return &ControlServerSideSorting{SortKeys: sortKeys}
|
||||
}
|
||||
|
||||
func (c *ControlServerSideSorting) Encode() *ber.Packet {
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||||
control := ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.GetControlType(), "Control Type")
|
||||
|
||||
value := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value")
|
||||
seqs := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "SortKeyList")
|
||||
|
||||
for _, f := range c.SortKeys {
|
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "")
|
||||
|
||||
seq.AppendChild(
|
||||
ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, f.AttributeType, "attributeType"),
|
||||
)
|
||||
seq.AppendChild(
|
||||
ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, f.MatchingRule, "orderingRule"),
|
||||
)
|
||||
if f.Reverse {
|
||||
seq.AppendChild(
|
||||
ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, 1, f.Reverse, "reverseOrder"),
|
||||
)
|
||||
}
|
||||
|
||||
seqs.AppendChild(seq)
|
||||
}
|
||||
|
||||
value.AppendChild(seqs)
|
||||
|
||||
packet.AppendChild(control)
|
||||
packet.AppendChild(value)
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func (c *ControlServerSideSorting) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality:%t %+v",
|
||||
"Server Side Sorting",
|
||||
c.GetControlType(),
|
||||
false,
|
||||
c.SortKeys,
|
||||
)
|
||||
}
|
||||
|
||||
// ControlServerSideSortingResponse
|
||||
|
||||
const (
|
||||
ControlServerSideSortingCodeSuccess ControlServerSideSortingCode = 0
|
||||
ControlServerSideSortingCodeOperationsError ControlServerSideSortingCode = 1
|
||||
ControlServerSideSortingCodeTimeLimitExceeded ControlServerSideSortingCode = 2
|
||||
ControlServerSideSortingCodeStrongAuthRequired ControlServerSideSortingCode = 8
|
||||
ControlServerSideSortingCodeAdminLimitExceeded ControlServerSideSortingCode = 11
|
||||
ControlServerSideSortingCodeNoSuchAttribute ControlServerSideSortingCode = 16
|
||||
ControlServerSideSortingCodeInappropriateMatching ControlServerSideSortingCode = 18
|
||||
ControlServerSideSortingCodeInsufficientAccessRights ControlServerSideSortingCode = 50
|
||||
ControlServerSideSortingCodeBusy ControlServerSideSortingCode = 51
|
||||
ControlServerSideSortingCodeUnwillingToPerform ControlServerSideSortingCode = 53
|
||||
ControlServerSideSortingCodeOther ControlServerSideSortingCode = 80
|
||||
)
|
||||
|
||||
var ControlServerSideSortingCodes = []ControlServerSideSortingCode{
|
||||
ControlServerSideSortingCodeSuccess,
|
||||
ControlServerSideSortingCodeOperationsError,
|
||||
ControlServerSideSortingCodeTimeLimitExceeded,
|
||||
ControlServerSideSortingCodeStrongAuthRequired,
|
||||
ControlServerSideSortingCodeAdminLimitExceeded,
|
||||
ControlServerSideSortingCodeNoSuchAttribute,
|
||||
ControlServerSideSortingCodeInappropriateMatching,
|
||||
ControlServerSideSortingCodeInsufficientAccessRights,
|
||||
ControlServerSideSortingCodeBusy,
|
||||
ControlServerSideSortingCodeUnwillingToPerform,
|
||||
ControlServerSideSortingCodeOther,
|
||||
}
|
||||
|
||||
type ControlServerSideSortingCode int64
|
||||
|
||||
// Valid test the code contained in the control against the ControlServerSideSortingCodes slice and return an error if the code is unknown.
|
||||
func (c ControlServerSideSortingCode) Valid() error {
|
||||
for _, validRet := range ControlServerSideSortingCodes {
|
||||
if c == validRet {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown return code : %d", c)
|
||||
}
|
||||
|
||||
func NewControlServerSideSortingResult(pkt *ber.Packet) (*ControlServerSideSortingResult, error) {
|
||||
control := &ControlServerSideSortingResult{}
|
||||
|
||||
if pkt == nil || len(pkt.Children) == 0 {
|
||||
return nil, fmt.Errorf("bad packet")
|
||||
}
|
||||
|
||||
codeInt, err := ber.ParseInt64(pkt.Children[0].Data.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
code := ControlServerSideSortingCode(codeInt)
|
||||
if err := code.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return control, nil
|
||||
}
|
||||
|
||||
type ControlServerSideSortingResult struct {
|
||||
Criticality bool
|
||||
|
||||
Result ControlServerSideSortingCode
|
||||
|
||||
// Not populated for now. I can't get openldap to send me this value, so I think this is specific to other directory server
|
||||
// AttributeType string
|
||||
}
|
||||
|
||||
func (control *ControlServerSideSortingResult) GetControlType() string {
|
||||
return ControlTypeServerSideSortingResult
|
||||
}
|
||||
|
||||
func (c *ControlServerSideSortingResult) Encode() *ber.Packet {
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "SortResult sequence")
|
||||
sortResult := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, int64(c.Result), "SortResult")
|
||||
packet.AppendChild(sortResult)
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func (c *ControlServerSideSortingResult) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality:%t ResultCode:%+v",
|
||||
"Server Side Sorting Result",
|
||||
c.GetControlType(),
|
||||
c.Criticality,
|
||||
c.Result,
|
||||
)
|
||||
}
|
||||
|
||||
// Mode for ControlTypeSyncRequest
|
||||
type ControlSyncRequestMode int64
|
||||
|
||||
const (
|
||||
SyncRequestModeRefreshOnly ControlSyncRequestMode = 1
|
||||
SyncRequestModeRefreshAndPersist ControlSyncRequestMode = 3
|
||||
)
|
||||
|
||||
// ControlSyncRequest implements the Sync Request Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncRequest struct {
|
||||
Criticality bool
|
||||
Mode ControlSyncRequestMode
|
||||
Cookie []byte
|
||||
ReloadHint bool
|
||||
}
|
||||
|
||||
func NewControlSyncRequest(
|
||||
mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
|
||||
) *ControlSyncRequest {
|
||||
return &ControlSyncRequest{
|
||||
Criticality: true,
|
||||
Mode: mode,
|
||||
Cookie: cookie,
|
||||
ReloadHint: reloadHint,
|
||||
}
|
||||
}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlSyncRequest) GetControlType() string {
|
||||
return ControlTypeSyncRequest
|
||||
}
|
||||
|
||||
// Encode encodes the control
|
||||
func (c *ControlSyncRequest) Encode() *ber.Packet {
|
||||
_mode := int64(c.Mode)
|
||||
mode := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, _mode, "Mode")
|
||||
var cookie *ber.Packet
|
||||
if len(c.Cookie) > 0 {
|
||||
cookie = ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
|
||||
cookie.Value = c.Cookie
|
||||
cookie.Data.Write(c.Cookie)
|
||||
}
|
||||
reloadHint := ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.ReloadHint, "Reload Hint")
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSyncRequest, "Control Type ("+ControlTypeMap[ControlTypeSyncRequest]+")"))
|
||||
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
||||
|
||||
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Sync Request)")
|
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Sync Request Value")
|
||||
seq.AppendChild(mode)
|
||||
if cookie != nil {
|
||||
seq.AppendChild(cookie)
|
||||
}
|
||||
seq.AppendChild(reloadHint)
|
||||
val.AppendChild(seq)
|
||||
|
||||
packet.AppendChild(val)
|
||||
return packet
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncRequest) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t Mode: %d Cookie: %s ReloadHint: %t",
|
||||
ControlTypeMap[ControlTypeSyncRequest],
|
||||
ControlTypeSyncRequest,
|
||||
c.Criticality,
|
||||
c.Mode,
|
||||
string(c.Cookie),
|
||||
c.ReloadHint,
|
||||
)
|
||||
}
|
||||
|
||||
// State for ControlSyncState
|
||||
type ControlSyncStateState int64
|
||||
|
||||
const (
|
||||
SyncStatePresent ControlSyncStateState = 0
|
||||
SyncStateAdd ControlSyncStateState = 1
|
||||
SyncStateModify ControlSyncStateState = 2
|
||||
SyncStateDelete ControlSyncStateState = 3
|
||||
)
|
||||
|
||||
// ControlSyncState implements the Sync State Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncState struct {
|
||||
Criticality bool
|
||||
State ControlSyncStateState
|
||||
EntryUUID uuid.UUID
|
||||
Cookie []byte
|
||||
}
|
||||
|
||||
func NewControlSyncState(pkt *ber.Packet) (*ControlSyncState, error) {
|
||||
var (
|
||||
state ControlSyncStateState
|
||||
entryUUID uuid.UUID
|
||||
cookie []byte
|
||||
err error
|
||||
)
|
||||
switch len(pkt.Children) {
|
||||
case 0, 1:
|
||||
return nil, fmt.Errorf("at least two children are required: %d", len(pkt.Children))
|
||||
case 2:
|
||||
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
|
||||
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||||
}
|
||||
case 3:
|
||||
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
|
||||
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||||
}
|
||||
cookie = pkt.Children[2].ByteValue
|
||||
}
|
||||
return &ControlSyncState{
|
||||
Criticality: false,
|
||||
State: state,
|
||||
EntryUUID: entryUUID,
|
||||
Cookie: cookie,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlSyncState) GetControlType() string {
|
||||
return ControlTypeSyncState
|
||||
}
|
||||
|
||||
// Encode encodes the control
|
||||
func (c *ControlSyncState) Encode() *ber.Packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncState) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t State: %d EntryUUID: %s Cookie: %s",
|
||||
ControlTypeMap[ControlTypeSyncState],
|
||||
ControlTypeSyncState,
|
||||
c.Criticality,
|
||||
c.State,
|
||||
c.EntryUUID.String(),
|
||||
string(c.Cookie),
|
||||
)
|
||||
}
|
||||
|
||||
// ControlSyncDone implements the Sync Done Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncDone struct {
|
||||
Criticality bool
|
||||
Cookie []byte
|
||||
RefreshDeletes bool
|
||||
}
|
||||
|
||||
func NewControlSyncDone(pkt *ber.Packet) (*ControlSyncDone, error) {
|
||||
var (
|
||||
cookie []byte
|
||||
refreshDeletes bool
|
||||
)
|
||||
switch len(pkt.Children) {
|
||||
case 0:
|
||||
// have nothing to do
|
||||
case 1:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
case 2:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
refreshDeletes = pkt.Children[1].Value.(bool)
|
||||
}
|
||||
return &ControlSyncDone{
|
||||
Criticality: false,
|
||||
Cookie: cookie,
|
||||
RefreshDeletes: refreshDeletes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlSyncDone) GetControlType() string {
|
||||
return ControlTypeSyncDone
|
||||
}
|
||||
|
||||
// Encode encodes the control
|
||||
func (c *ControlSyncDone) Encode() *ber.Packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncDone) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t Cookie: %s RefreshDeletes: %t",
|
||||
ControlTypeMap[ControlTypeSyncDone],
|
||||
ControlTypeSyncDone,
|
||||
c.Criticality,
|
||||
string(c.Cookie),
|
||||
c.RefreshDeletes,
|
||||
)
|
||||
}
|
||||
|
||||
// Tag For ControlSyncInfo
|
||||
type ControlSyncInfoValue uint64
|
||||
|
||||
const (
|
||||
SyncInfoNewcookie ControlSyncInfoValue = 0
|
||||
SyncInfoRefreshDelete ControlSyncInfoValue = 1
|
||||
SyncInfoRefreshPresent ControlSyncInfoValue = 2
|
||||
SyncInfoSyncIdSet ControlSyncInfoValue = 3
|
||||
)
|
||||
|
||||
// ControlSyncInfoNewCookie implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncInfoNewCookie struct {
|
||||
Cookie []byte
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncInfoNewCookie) String() string {
|
||||
return fmt.Sprintf(
|
||||
"NewCookie[Cookie: %s]",
|
||||
string(c.Cookie),
|
||||
)
|
||||
}
|
||||
|
||||
// ControlSyncInfoRefreshDelete implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncInfoRefreshDelete struct {
|
||||
Cookie []byte
|
||||
RefreshDone bool
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncInfoRefreshDelete) String() string {
|
||||
return fmt.Sprintf(
|
||||
"RefreshDelete[Cookie: %s RefreshDone: %t]",
|
||||
string(c.Cookie),
|
||||
c.RefreshDone,
|
||||
)
|
||||
}
|
||||
|
||||
// ControlSyncInfoRefreshPresent implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncInfoRefreshPresent struct {
|
||||
Cookie []byte
|
||||
RefreshDone bool
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncInfoRefreshPresent) String() string {
|
||||
return fmt.Sprintf(
|
||||
"RefreshPresent[Cookie: %s RefreshDone: %t]",
|
||||
string(c.Cookie),
|
||||
c.RefreshDone,
|
||||
)
|
||||
}
|
||||
|
||||
// ControlSyncInfoSyncIdSet implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncInfoSyncIdSet struct {
|
||||
Cookie []byte
|
||||
RefreshDeletes bool
|
||||
SyncUUIDs []uuid.UUID
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncInfoSyncIdSet) String() string {
|
||||
return fmt.Sprintf(
|
||||
"SyncIdSet[Cookie: %s RefreshDeletes: %t SyncUUIDs: %v]",
|
||||
string(c.Cookie),
|
||||
c.RefreshDeletes,
|
||||
c.SyncUUIDs,
|
||||
)
|
||||
}
|
||||
|
||||
// ControlSyncInfo implements the Sync Info Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||||
type ControlSyncInfo struct {
|
||||
Criticality bool
|
||||
Value ControlSyncInfoValue
|
||||
NewCookie *ControlSyncInfoNewCookie
|
||||
RefreshDelete *ControlSyncInfoRefreshDelete
|
||||
RefreshPresent *ControlSyncInfoRefreshPresent
|
||||
SyncIdSet *ControlSyncInfoSyncIdSet
|
||||
}
|
||||
|
||||
func NewControlSyncInfo(pkt *ber.Packet) (*ControlSyncInfo, error) {
|
||||
var (
|
||||
cookie []byte
|
||||
refreshDone = true
|
||||
refreshDeletes bool
|
||||
syncUUIDs []uuid.UUID
|
||||
)
|
||||
c := &ControlSyncInfo{Criticality: false}
|
||||
switch ControlSyncInfoValue(pkt.Identifier.Tag) {
|
||||
case SyncInfoNewcookie:
|
||||
c.Value = SyncInfoNewcookie
|
||||
c.NewCookie = &ControlSyncInfoNewCookie{
|
||||
Cookie: pkt.ByteValue,
|
||||
}
|
||||
case SyncInfoRefreshDelete:
|
||||
c.Value = SyncInfoRefreshDelete
|
||||
switch len(pkt.Children) {
|
||||
case 0:
|
||||
// have nothing to do
|
||||
case 1:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
case 2:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
refreshDone = pkt.Children[1].Value.(bool)
|
||||
}
|
||||
c.RefreshDelete = &ControlSyncInfoRefreshDelete{
|
||||
Cookie: cookie,
|
||||
RefreshDone: refreshDone,
|
||||
}
|
||||
case SyncInfoRefreshPresent:
|
||||
c.Value = SyncInfoRefreshPresent
|
||||
switch len(pkt.Children) {
|
||||
case 0:
|
||||
// have nothing to do
|
||||
case 1:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
case 2:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
refreshDone = pkt.Children[1].Value.(bool)
|
||||
}
|
||||
c.RefreshPresent = &ControlSyncInfoRefreshPresent{
|
||||
Cookie: cookie,
|
||||
RefreshDone: refreshDone,
|
||||
}
|
||||
case SyncInfoSyncIdSet:
|
||||
c.Value = SyncInfoSyncIdSet
|
||||
switch len(pkt.Children) {
|
||||
case 0:
|
||||
// have nothing to do
|
||||
case 1:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
case 2:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
refreshDeletes = pkt.Children[1].Value.(bool)
|
||||
case 3:
|
||||
cookie = pkt.Children[0].ByteValue
|
||||
refreshDeletes = pkt.Children[1].Value.(bool)
|
||||
syncUUIDs = make([]uuid.UUID, 0, len(pkt.Children[2].Children))
|
||||
for _, child := range pkt.Children[2].Children {
|
||||
u, err := uuid.FromBytes(child.ByteValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||||
}
|
||||
syncUUIDs = append(syncUUIDs, u)
|
||||
}
|
||||
}
|
||||
c.SyncIdSet = &ControlSyncInfoSyncIdSet{
|
||||
Cookie: cookie,
|
||||
RefreshDeletes: refreshDeletes,
|
||||
SyncUUIDs: syncUUIDs,
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown sync info value: %d", pkt.Identifier.Tag)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetControlType returns the OID
|
||||
func (c *ControlSyncInfo) GetControlType() string {
|
||||
return ControlTypeSyncInfo
|
||||
}
|
||||
|
||||
// Encode encodes the control
|
||||
func (c *ControlSyncInfo) Encode() *ber.Packet {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a human-readable description
|
||||
func (c *ControlSyncInfo) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Control Type: %s (%q) Criticality: %t Value: %d %s %s %s %s",
|
||||
ControlTypeMap[ControlTypeSyncInfo],
|
||||
ControlTypeSyncInfo,
|
||||
c.Criticality,
|
||||
c.Value,
|
||||
c.NewCookie,
|
||||
c.RefreshDelete,
|
||||
c.RefreshPresent,
|
||||
c.SyncIdSet,
|
||||
)
|
||||
}
|
||||
|
|
8
vendor/github.com/go-ldap/ldap/v3/debug.go
generated
vendored
8
vendor/github.com/go-ldap/ldap/v3/debug.go
generated
vendored
|
@ -1,13 +1,11 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
// debugging type
|
||||
// - has a Printf method to write the debug output
|
||||
// - has a Printf method to write the debug output
|
||||
type debugging bool
|
||||
|
||||
// Enable controls debugging mode.
|
||||
|
@ -18,13 +16,13 @@ func (debug *debugging) Enable(b bool) {
|
|||
// Printf writes debug output.
|
||||
func (debug debugging) Printf(format string, args ...interface{}) {
|
||||
if debug {
|
||||
log.Printf(format, args...)
|
||||
logger.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintPacket dumps a packet.
|
||||
func (debug debugging) PrintPacket(packet *ber.Packet) {
|
||||
if debug {
|
||||
ber.WritePacket(log.Writer(), packet)
|
||||
ber.WritePacket(logger.Writer(), packet)
|
||||
}
|
||||
}
|
||||
|
|
6
vendor/github.com/go-ldap/ldap/v3/del.go
generated
vendored
6
vendor/github.com/go-ldap/ldap/v3/del.go
generated
vendored
|
@ -1,8 +1,7 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fmt"
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
|
@ -53,7 +52,8 @@ func (l *Conn) Del(delRequest *DelRequest) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||
return fmt.Errorf("ldap: unexpected response: %d", packet.Children[1].Tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
92
vendor/github.com/go-ldap/ldap/v3/dn.go
generated
vendored
92
vendor/github.com/go-ldap/ldap/v3/dn.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
|||
enchex "encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
|
@ -18,16 +19,95 @@ type AttributeTypeAndValue struct {
|
|||
Value string
|
||||
}
|
||||
|
||||
// String returns a normalized string representation of this attribute type and
|
||||
// value pair which is the a lowercased join of the Type and Value with a "=".
|
||||
func (a *AttributeTypeAndValue) String() string {
|
||||
return strings.ToLower(a.Type) + "=" + a.encodeValue()
|
||||
}
|
||||
|
||||
func (a *AttributeTypeAndValue) encodeValue() string {
|
||||
// Normalize the value first.
|
||||
// value := strings.ToLower(a.Value)
|
||||
value := a.Value
|
||||
|
||||
encodedBuf := bytes.Buffer{}
|
||||
|
||||
escapeChar := func(c byte) {
|
||||
encodedBuf.WriteByte('\\')
|
||||
encodedBuf.WriteByte(c)
|
||||
}
|
||||
|
||||
escapeHex := func(c byte) {
|
||||
encodedBuf.WriteByte('\\')
|
||||
encodedBuf.WriteString(enchex.EncodeToString([]byte{c}))
|
||||
}
|
||||
|
||||
for i := 0; i < len(value); i++ {
|
||||
char := value[i]
|
||||
if i == 0 && char == ' ' || char == '#' {
|
||||
// Special case leading space or number sign.
|
||||
escapeChar(char)
|
||||
continue
|
||||
}
|
||||
if i == len(value)-1 && char == ' ' {
|
||||
// Special case trailing space.
|
||||
escapeChar(char)
|
||||
continue
|
||||
}
|
||||
|
||||
switch char {
|
||||
case '"', '+', ',', ';', '<', '>', '\\':
|
||||
// Each of these special characters must be escaped.
|
||||
escapeChar(char)
|
||||
continue
|
||||
}
|
||||
|
||||
if char < ' ' || char > '~' {
|
||||
// All special character escapes are handled first
|
||||
// above. All bytes less than ASCII SPACE and all bytes
|
||||
// greater than ASCII TILDE must be hex-escaped.
|
||||
escapeHex(char)
|
||||
continue
|
||||
}
|
||||
|
||||
// Any other character does not require escaping.
|
||||
encodedBuf.WriteByte(char)
|
||||
}
|
||||
|
||||
return encodedBuf.String()
|
||||
}
|
||||
|
||||
// RelativeDN represents a relativeDistinguishedName from https://tools.ietf.org/html/rfc4514
|
||||
type RelativeDN struct {
|
||||
Attributes []*AttributeTypeAndValue
|
||||
}
|
||||
|
||||
// String returns a normalized string representation of this relative DN which
|
||||
// is the a join of all attributes (sorted in increasing order) with a "+".
|
||||
func (r *RelativeDN) String() string {
|
||||
attrs := make([]string, len(r.Attributes))
|
||||
for i := range r.Attributes {
|
||||
attrs[i] = r.Attributes[i].String()
|
||||
}
|
||||
sort.Strings(attrs)
|
||||
return strings.Join(attrs, "+")
|
||||
}
|
||||
|
||||
// DN represents a distinguishedName from https://tools.ietf.org/html/rfc4514
|
||||
type DN struct {
|
||||
RDNs []*RelativeDN
|
||||
}
|
||||
|
||||
// String returns a normalized string representation of this DN which is the
|
||||
// join of all relative DNs with a ",".
|
||||
func (d *DN) String() string {
|
||||
rdns := make([]string, len(d.RDNs))
|
||||
for i := range d.RDNs {
|
||||
rdns[i] = d.RDNs[i].String()
|
||||
}
|
||||
return strings.Join(rdns, ",")
|
||||
}
|
||||
|
||||
// ParseDN returns a distinguishedName or an error.
|
||||
// The function respects https://tools.ietf.org/html/rfc4514
|
||||
func ParseDN(str string) (*DN, error) {
|
||||
|
@ -76,7 +156,7 @@ func ParseDN(str string) (*DN, error) {
|
|||
case char == '\\':
|
||||
unescapedTrailingSpaces = 0
|
||||
escaping = true
|
||||
case char == '=':
|
||||
case char == '=' && attribute.Type == "":
|
||||
attribute.Type = stringFromBuffer()
|
||||
// Special case: If the first character in the value is # the
|
||||
// following data is BER encoded so we can just fast forward
|
||||
|
@ -84,7 +164,7 @@ func ParseDN(str string) (*DN, error) {
|
|||
if len(str) > i+1 && str[i+1] == '#' {
|
||||
i += 2
|
||||
index := strings.IndexAny(str[i:], ",+")
|
||||
data := str
|
||||
var data string
|
||||
if index > 0 {
|
||||
data = str[i : i+index]
|
||||
} else {
|
||||
|
@ -101,7 +181,7 @@ func ParseDN(str string) (*DN, error) {
|
|||
buffer.WriteString(packet.Data.String())
|
||||
i += len(data) - 1
|
||||
}
|
||||
case char == ',' || char == '+':
|
||||
case char == ',' || char == '+' || char == ';':
|
||||
// We're done with this RDN or value, push it
|
||||
if len(attribute.Type) == 0 {
|
||||
return nil, errors.New("incomplete type, value pair")
|
||||
|
@ -109,7 +189,7 @@ func ParseDN(str string) (*DN, error) {
|
|||
attribute.Value = stringFromBuffer()
|
||||
rdn.Attributes = append(rdn.Attributes, attribute)
|
||||
attribute = new(AttributeTypeAndValue)
|
||||
if char == ',' {
|
||||
if char == ',' || char == ';' {
|
||||
dn.RDNs = append(dn.RDNs, rdn)
|
||||
rdn = new(RelativeDN)
|
||||
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
|
||||
|
@ -206,7 +286,7 @@ func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
|
|||
return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
|
||||
}
|
||||
|
||||
// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
|
||||
// EqualFold returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
|
||||
// Returns true if they have the same number of relative distinguished names
|
||||
// and corresponding relative distinguished names (by position) are the same.
|
||||
// Case of the attribute type and value is not significant
|
||||
|
@ -238,7 +318,7 @@ func (d *DN) AncestorOfFold(other *DN) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
|
||||
// EqualFold returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
|
||||
// Case of the attribute type is not significant
|
||||
func (r *RelativeDN) EqualFold(other *RelativeDN) bool {
|
||||
if len(r.Attributes) != len(other.Attributes) {
|
||||
|
|
26
vendor/github.com/go-ldap/ldap/v3/error.go
generated
vendored
26
vendor/github.com/go-ldap/ldap/v3/error.go
generated
vendored
|
@ -192,6 +192,8 @@ func (e *Error) Error() string {
|
|||
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error { return e.Err }
|
||||
|
||||
// GetLDAPError creates an Error out of a BER packet representing a LDAPResult
|
||||
// The return is an error object. It can be casted to a Error structure.
|
||||
// This function returns nil if resultCode in the LDAPResult sequence is success(0).
|
||||
|
@ -206,15 +208,21 @@ func GetLDAPError(packet *ber.Packet) error {
|
|||
return &Error{ResultCode: ErrorUnexpectedResponse, Err: fmt.Errorf("Empty response in packet"), Packet: packet}
|
||||
}
|
||||
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
|
||||
resultCode := uint16(response.Children[0].Value.(int64))
|
||||
if resultCode == 0 { // No error
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
ResultCode: resultCode,
|
||||
MatchedDN: response.Children[1].Value.(string),
|
||||
Err: fmt.Errorf("%s", response.Children[2].Value.(string)),
|
||||
Packet: packet,
|
||||
if ber.Type(response.Children[0].Tag) == ber.Type(ber.TagInteger) || ber.Type(response.Children[0].Tag) == ber.Type(ber.TagEnumerated) {
|
||||
resultCode := uint16(response.Children[0].Value.(int64))
|
||||
if resultCode == 0 { // No error
|
||||
return nil
|
||||
}
|
||||
|
||||
if ber.Type(response.Children[1].Tag) == ber.Type(ber.TagOctetString) &&
|
||||
ber.Type(response.Children[2].Tag) == ber.Type(ber.TagOctetString) {
|
||||
return &Error{
|
||||
ResultCode: resultCode,
|
||||
MatchedDN: response.Children[1].Value.(string),
|
||||
Err: fmt.Errorf("%s", response.Children[2].Value.(string)),
|
||||
Packet: packet,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
3
vendor/github.com/go-ldap/ldap/v3/filter.go
generated
vendored
3
vendor/github.com/go-ldap/ldap/v3/filter.go
generated
vendored
|
@ -396,7 +396,7 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
|
|||
|
||||
case packet.Tag == FilterEqualityMatch && bytes.Equal(condition.Bytes(), _SymbolAny):
|
||||
packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute.String(), FilterMap[FilterPresent])
|
||||
case packet.Tag == FilterEqualityMatch && bytes.Index(condition.Bytes(), _SymbolAny) > -1:
|
||||
case packet.Tag == FilterEqualityMatch && bytes.Contains(condition.Bytes(), _SymbolAny):
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute.String(), "Attribute"))
|
||||
packet.Tag = FilterSubstrings
|
||||
packet.Description = FilterMap[uint64(packet.Tag)]
|
||||
|
@ -438,7 +438,6 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
|
|||
|
||||
// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
|
||||
func decodeEscapedSymbols(src []byte) (string, error) {
|
||||
|
||||
var (
|
||||
buffer bytes.Buffer
|
||||
offset int
|
||||
|
|
57
vendor/github.com/go-ldap/ldap/v3/ldap.go
generated
vendored
57
vendor/github.com/go-ldap/ldap/v3/ldap.go
generated
vendored
|
@ -3,7 +3,9 @@ package ldap
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
@ -30,6 +32,7 @@ const (
|
|||
ApplicationSearchResultReference = 19
|
||||
ApplicationExtendedRequest = 23
|
||||
ApplicationExtendedResponse = 24
|
||||
ApplicationIntermediateResponse = 25
|
||||
)
|
||||
|
||||
// ApplicationMap contains human readable descriptions of LDAP Application Codes
|
||||
|
@ -54,6 +57,7 @@ var ApplicationMap = map[uint8]string{
|
|||
ApplicationSearchResultReference: "Search Result Reference",
|
||||
ApplicationExtendedRequest: "Extended Request",
|
||||
ApplicationExtendedResponse: "Extended Response",
|
||||
ApplicationIntermediateResponse: "Intermediate Response",
|
||||
}
|
||||
|
||||
// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
|
||||
|
@ -82,6 +86,13 @@ var BeheraPasswordPolicyErrorMap = map[int8]string{
|
|||
BeheraPasswordInHistory: "New password is in list of old passwords",
|
||||
}
|
||||
|
||||
var logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
||||
// Logger allows clients to override the default logger
|
||||
func Logger(l *log.Logger) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
// Adds descriptions to an LDAP Response packet for debugging
|
||||
func addLDAPDescriptions(packet *ber.Packet) (err error) {
|
||||
defer func() {
|
||||
|
@ -221,18 +232,18 @@ func addControlDescriptions(packet *ber.Packet) error {
|
|||
sequence := value.Children[0]
|
||||
for _, child := range sequence.Children {
|
||||
if child.Tag == 0 {
|
||||
//Warning
|
||||
// Warning
|
||||
warningPacket := child.Children[0]
|
||||
val, err := ber.ParseInt64(warningPacket.Data.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode data bytes: %s", err)
|
||||
}
|
||||
if warningPacket.Tag == 0 {
|
||||
//timeBeforeExpiration
|
||||
// timeBeforeExpiration
|
||||
value.Description += " (TimeBeforeExpiration)"
|
||||
warningPacket.Value = val
|
||||
} else if warningPacket.Tag == 1 {
|
||||
//graceAuthNsRemaining
|
||||
// graceAuthNsRemaining
|
||||
value.Description += " (GraceAuthNsRemaining)"
|
||||
warningPacket.Value = val
|
||||
}
|
||||
|
@ -337,3 +348,43 @@ func EscapeFilter(filter string) string {
|
|||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// EscapeDN escapes distinguished names as described in RFC4514. Characters in the
|
||||
// set `"+,;<>\` are escaped by prepending a backslash, which is also done for trailing
|
||||
// spaces or a leading `#`. Null bytes are replaced with `\00`.
|
||||
func EscapeDN(dn string) string {
|
||||
if dn == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
builder := strings.Builder{}
|
||||
|
||||
for i, r := range dn {
|
||||
// Escape leading and trailing spaces
|
||||
if (i == 0 || i == len(dn)-1) && r == ' ' {
|
||||
builder.WriteRune('\\')
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
// Escape leading '#'
|
||||
if i == 0 && r == '#' {
|
||||
builder.WriteRune('\\')
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
// Escape characters as defined in RFC4514
|
||||
switch r {
|
||||
case '"', '+', ',', ';', '<', '>', '\\':
|
||||
builder.WriteRune('\\')
|
||||
builder.WriteRune(r)
|
||||
case '\x00': // Null byte may not be escaped by a leading backslash
|
||||
builder.WriteString("\\00")
|
||||
default:
|
||||
builder.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
|
10
vendor/github.com/go-ldap/ldap/v3/moddn.go
generated
vendored
10
vendor/github.com/go-ldap/ldap/v3/moddn.go
generated
vendored
|
@ -1,8 +1,7 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fmt"
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
|
@ -25,7 +24,9 @@ type ModifyDNRequest struct {
|
|||
// RDN of the given DN.
|
||||
//
|
||||
// A call like
|
||||
// mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "")
|
||||
//
|
||||
// mdnReq := NewModifyDNRequest("uid=someone,dc=example,dc=org", "uid=newname", true, "")
|
||||
//
|
||||
// will setup the request to just rename uid=someone,dc=example,dc=org to
|
||||
// uid=newname,dc=example,dc=org.
|
||||
func NewModifyDNRequest(dn string, rdn string, delOld bool, newSup string) *ModifyDNRequest {
|
||||
|
@ -94,7 +95,8 @@ func (l *Conn) ModifyDN(m *ModifyDNRequest) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||
return fmt.Errorf("ldap: unexpected response: %d", packet.Children[1].Tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
14
vendor/github.com/go-ldap/ldap/v3/modify.go
generated
vendored
14
vendor/github.com/go-ldap/ldap/v3/modify.go
generated
vendored
|
@ -2,7 +2,7 @@ package ldap
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"fmt"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
@ -127,8 +127,9 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
|
||||
return fmt.Errorf("ldap: unexpected response: %d", packet.Children[1].Tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -136,6 +137,8 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error {
|
|||
type ModifyResult struct {
|
||||
// Controls are the returned controls
|
||||
Controls []Control
|
||||
// Referral is the returned referral
|
||||
Referral string
|
||||
}
|
||||
|
||||
// ModifyWithResult performs the ModifyRequest and returns the result
|
||||
|
@ -158,9 +161,10 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er
|
|||
|
||||
switch packet.Children[1].Tag {
|
||||
case ApplicationModifyResponse:
|
||||
err := GetLDAPError(packet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err = GetLDAPError(packet); err != nil {
|
||||
result.Referral = getReferral(err, packet)
|
||||
|
||||
return result, err
|
||||
}
|
||||
if len(packet.Children) == 3 {
|
||||
for _, child := range packet.Children[2].Children {
|
||||
|
|
17
vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
generated
vendored
17
vendor/github.com/go-ldap/ldap/v3/passwdmodify.go
generated
vendored
|
@ -70,7 +70,6 @@ func (req *PasswordModifyRequest) appendTo(envelope *ber.Packet) error {
|
|||
// newPassword is the desired user's password. If empty the server can return
|
||||
// an error or generate a new password that will be available in the
|
||||
// PasswordModifyResult.GeneratedPassword
|
||||
//
|
||||
func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
|
||||
return &PasswordModifyRequest{
|
||||
UserIdentity: userIdentity,
|
||||
|
@ -95,15 +94,9 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
|
|||
result := &PasswordModifyResult{}
|
||||
|
||||
if packet.Children[1].Tag == ApplicationExtendedResponse {
|
||||
err := GetLDAPError(packet)
|
||||
if err != nil {
|
||||
if IsErrorWithCode(err, LDAPResultReferral) {
|
||||
for _, child := range packet.Children[1].Children {
|
||||
if child.Tag == 3 {
|
||||
result.Referral = child.Children[0].Value.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = GetLDAPError(packet); err != nil {
|
||||
result.Referral = getReferral(err, packet)
|
||||
|
||||
return result, err
|
||||
}
|
||||
} else {
|
||||
|
@ -112,10 +105,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
|
|||
|
||||
extendedResponse := packet.Children[1]
|
||||
for _, child := range extendedResponse.Children {
|
||||
if child.Tag == 11 {
|
||||
if child.Tag == ber.TagEmbeddedPDV {
|
||||
passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
|
||||
if len(passwordModifyResponseValue.Children) == 1 {
|
||||
if passwordModifyResponseValue.Children[0].Tag == 0 {
|
||||
if passwordModifyResponseValue.Children[0].Tag == ber.TagEOC {
|
||||
result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
|
||||
}
|
||||
}
|
||||
|
|
41
vendor/github.com/go-ldap/ldap/v3/request.go
generated
vendored
41
vendor/github.com/go-ldap/ldap/v3/request.go
generated
vendored
|
@ -9,7 +9,8 @@ import (
|
|||
var (
|
||||
errRespChanClosed = errors.New("ldap: response channel closed")
|
||||
errCouldNotRetMsg = errors.New("ldap: could not retrieve message")
|
||||
ErrNilConnection = errors.New("ldap: conn is nil, expected net.Conn")
|
||||
// ErrNilConnection is returned if doRequest is called with a nil connection.
|
||||
ErrNilConnection = errors.New("ldap: conn is nil, expected net.Conn")
|
||||
)
|
||||
|
||||
type request interface {
|
||||
|
@ -69,3 +70,41 @@ func (l *Conn) readPacket(msgCtx *messageContext) (*ber.Packet, error) {
|
|||
}
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
func getReferral(err error, packet *ber.Packet) (referral string) {
|
||||
if !IsErrorWithCode(err, LDAPResultReferral) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(packet.Children) < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// The packet Tag itself (of child 2) is generally a ber.TagObjectDescriptor with referrals however OpenLDAP
|
||||
// seemingly returns a ber.Tag.GeneralizedTime. Every currently tested LDAP server which returns referrals returns
|
||||
// an ASN.1 BER packet with the Type of ber.TypeConstructed and Class of ber.ClassApplication however. Thus this
|
||||
// check expressly checks these fields instead.
|
||||
//
|
||||
// Related Issues:
|
||||
// - https://github.com/authelia/authelia/issues/4199 (downstream)
|
||||
if len(packet.Children[1].Children) == 0 || (packet.Children[1].TagType != ber.TypeConstructed || packet.Children[1].ClassType != ber.ClassApplication) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var ok bool
|
||||
|
||||
for _, child := range packet.Children[1].Children {
|
||||
// The referral URI itself should be contained within a child which has a Tag of ber.BitString or
|
||||
// ber.TagPrintableString, and the Type of ber.TypeConstructed and the Class of ClassContext. As soon as any of
|
||||
// these conditions is not true we can skip this child.
|
||||
if (child.Tag != ber.TagBitString && child.Tag != ber.TagPrintableString) || child.TagType != ber.TypeConstructed || child.ClassType != ber.ClassContext {
|
||||
continue
|
||||
}
|
||||
|
||||
if referral, ok = child.Children[0].Value.(string); ok {
|
||||
return referral
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
|
207
vendor/github.com/go-ldap/ldap/v3/response.go
generated
vendored
Normal file
207
vendor/github.com/go-ldap/ldap/v3/response.go
generated
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
// Response defines an interface to get data from an LDAP server
|
||||
type Response interface {
|
||||
Entry() *Entry
|
||||
Referral() string
|
||||
Controls() []Control
|
||||
Err() error
|
||||
Next() bool
|
||||
}
|
||||
|
||||
type searchResponse struct {
|
||||
conn *Conn
|
||||
ch chan *SearchSingleResult
|
||||
|
||||
entry *Entry
|
||||
referral string
|
||||
controls []Control
|
||||
err error
|
||||
}
|
||||
|
||||
// Entry returns an entry from the given search request
|
||||
func (r *searchResponse) Entry() *Entry {
|
||||
return r.entry
|
||||
}
|
||||
|
||||
// Referral returns a referral from the given search request
|
||||
func (r *searchResponse) Referral() string {
|
||||
return r.referral
|
||||
}
|
||||
|
||||
// Controls returns controls from the given search request
|
||||
func (r *searchResponse) Controls() []Control {
|
||||
return r.controls
|
||||
}
|
||||
|
||||
// Err returns an error when the given search request was failed
|
||||
func (r *searchResponse) Err() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// Next returns whether next data exist or not
|
||||
func (r *searchResponse) Next() bool {
|
||||
res, ok := <-r.ch
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if res == nil {
|
||||
return false
|
||||
}
|
||||
r.err = res.Error
|
||||
if r.err != nil {
|
||||
return false
|
||||
}
|
||||
r.entry = res.Entry
|
||||
r.referral = res.Referral
|
||||
r.controls = res.Controls
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest) {
|
||||
go func() {
|
||||
defer func() {
|
||||
close(r.ch)
|
||||
if err := recover(); err != nil {
|
||||
r.conn.err = fmt.Errorf("ldap: recovered panic in searchResponse: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if r.conn.IsClosing() {
|
||||
return
|
||||
}
|
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, r.conn.nextMessageID(), "MessageID"))
|
||||
// encode search request
|
||||
err := searchRequest.appendTo(packet)
|
||||
if err != nil {
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
r.conn.Debug.PrintPacket(packet)
|
||||
|
||||
msgCtx, err := r.conn.sendMessage(packet)
|
||||
if err != nil {
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
defer r.conn.finishMessage(msgCtx)
|
||||
|
||||
foundSearchSingleResultDone := false
|
||||
for !foundSearchSingleResultDone {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
r.conn.Debug.Printf("%d: %s", msgCtx.id, ctx.Err().Error())
|
||||
return
|
||||
default:
|
||||
r.conn.Debug.Printf("%d: waiting for response", msgCtx.id)
|
||||
packetResponse, ok := <-msgCtx.responses
|
||||
if !ok {
|
||||
err := NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
packet, err = packetResponse.ReadPacket()
|
||||
r.conn.Debug.Printf("%d: got response %p", msgCtx.id, packet)
|
||||
if err != nil {
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
|
||||
if r.conn.Debug {
|
||||
if err := addLDAPDescriptions(packet); err != nil {
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
ber.PrintPacket(packet)
|
||||
}
|
||||
|
||||
switch packet.Children[1].Tag {
|
||||
case ApplicationSearchResultEntry:
|
||||
result := &SearchSingleResult{
|
||||
Entry: &Entry{
|
||||
DN: packet.Children[1].Children[0].Value.(string),
|
||||
Attributes: unpackAttributes(packet.Children[1].Children[1].Children),
|
||||
},
|
||||
}
|
||||
if len(packet.Children) != 3 {
|
||||
r.ch <- result
|
||||
continue
|
||||
}
|
||||
decoded, err := DecodeControl(packet.Children[2].Children[0])
|
||||
if err != nil {
|
||||
werr := fmt.Errorf("failed to decode search result entry: %w", err)
|
||||
result.Error = werr
|
||||
r.ch <- result
|
||||
return
|
||||
}
|
||||
result.Controls = append(result.Controls, decoded)
|
||||
r.ch <- result
|
||||
|
||||
case ApplicationSearchResultDone:
|
||||
if err := GetLDAPError(packet); err != nil {
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
if len(packet.Children) == 3 {
|
||||
result := &SearchSingleResult{}
|
||||
for _, child := range packet.Children[2].Children {
|
||||
decodedChild, err := DecodeControl(child)
|
||||
if err != nil {
|
||||
werr := fmt.Errorf("failed to decode child control: %w", err)
|
||||
r.ch <- &SearchSingleResult{Error: werr}
|
||||
return
|
||||
}
|
||||
result.Controls = append(result.Controls, decodedChild)
|
||||
}
|
||||
r.ch <- result
|
||||
}
|
||||
foundSearchSingleResultDone = true
|
||||
|
||||
case ApplicationSearchResultReference:
|
||||
ref := packet.Children[1].Children[0].Value.(string)
|
||||
r.ch <- &SearchSingleResult{Referral: ref}
|
||||
|
||||
case ApplicationIntermediateResponse:
|
||||
decoded, err := DecodeControl(packet.Children[1])
|
||||
if err != nil {
|
||||
werr := fmt.Errorf("failed to decode intermediate response: %w", err)
|
||||
r.ch <- &SearchSingleResult{Error: werr}
|
||||
return
|
||||
}
|
||||
result := &SearchSingleResult{}
|
||||
result.Controls = append(result.Controls, decoded)
|
||||
r.ch <- result
|
||||
|
||||
default:
|
||||
err := fmt.Errorf("unknown tag: %d", packet.Children[1].Tag)
|
||||
r.ch <- &SearchSingleResult{Error: err}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
r.conn.Debug.Printf("%d: returning", msgCtx.id)
|
||||
}()
|
||||
}
|
||||
|
||||
func newSearchResponse(conn *Conn, bufferSize int) *searchResponse {
|
||||
var ch chan *SearchSingleResult
|
||||
if bufferSize > 0 {
|
||||
ch = make(chan *SearchSingleResult, bufferSize)
|
||||
} else {
|
||||
ch = make(chan *SearchSingleResult)
|
||||
}
|
||||
return &searchResponse{
|
||||
conn: conn,
|
||||
ch: ch,
|
||||
}
|
||||
}
|
303
vendor/github.com/go-ldap/ldap/v3/search.go
generated
vendored
303
vendor/github.com/go-ldap/ldap/v3/search.go
generated
vendored
|
@ -1,10 +1,14 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
@ -161,6 +165,155 @@ func (e *Entry) PrettyPrint(indent int) {
|
|||
}
|
||||
}
|
||||
|
||||
// Describe the tag to use for struct field tags
|
||||
const decoderTagName = "ldap"
|
||||
|
||||
// readTag will read the reflect.StructField value for
|
||||
// the key defined in decoderTagName. If omitempty is
|
||||
// specified, the field may not be filled.
|
||||
func readTag(f reflect.StructField) (string, bool) {
|
||||
val, ok := f.Tag.Lookup(decoderTagName)
|
||||
if !ok {
|
||||
return f.Name, false
|
||||
}
|
||||
opts := strings.Split(val, ",")
|
||||
omit := false
|
||||
if len(opts) == 2 {
|
||||
omit = opts[1] == "omitempty"
|
||||
}
|
||||
return opts[0], omit
|
||||
}
|
||||
|
||||
// Unmarshal parses the Entry in the value pointed to by i
|
||||
//
|
||||
// Currently, this methods only supports struct fields of type
|
||||
// string, []string, int, int64, []byte, *DN, []*DN or time.Time. Other field types
|
||||
// will not be regarded. If the field type is a string or int but multiple
|
||||
// attribute values are returned, the first value will be used to fill the field.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type UserEntry struct {
|
||||
// // Fields with the tag key `dn` are automatically filled with the
|
||||
// // objects distinguishedName. This can be used multiple times.
|
||||
// DN string `ldap:"dn"`
|
||||
//
|
||||
// // This field will be filled with the attribute value for
|
||||
// // userPrincipalName. An attribute can be read into a struct field
|
||||
// // multiple times. Missing attributes will not result in an error.
|
||||
// UserPrincipalName string `ldap:"userPrincipalName"`
|
||||
//
|
||||
// // memberOf may have multiple values. If you don't
|
||||
// // know the amount of attribute values at runtime, use a string array.
|
||||
// MemberOf []string `ldap:"memberOf"`
|
||||
//
|
||||
// // ID is an integer value, it will fail unmarshaling when the given
|
||||
// // attribute value cannot be parsed into an integer.
|
||||
// ID int `ldap:"id"`
|
||||
//
|
||||
// // LongID is similar to ID but uses an int64 instead.
|
||||
// LongID int64 `ldap:"longId"`
|
||||
//
|
||||
// // Data is similar to MemberOf a slice containing all attribute
|
||||
// // values.
|
||||
// Data []byte `ldap:"data"`
|
||||
//
|
||||
// // Time is parsed with the generalizedTime spec into a time.Time
|
||||
// Created time.Time `ldap:"createdTimestamp"`
|
||||
//
|
||||
// // *DN is parsed with the ParseDN
|
||||
// Owner *ldap.DN `ldap:"owner"`
|
||||
//
|
||||
// // []*DN is parsed with the ParseDN
|
||||
// Children []*ldap.DN `ldap:"children"`
|
||||
//
|
||||
// // This won't work, as the field is not of type string. For this
|
||||
// // to work, you'll have to temporarily store the result in string
|
||||
// // (or string array) and convert it to the desired type afterwards.
|
||||
// UserAccountControl uint32 `ldap:"userPrincipalName"`
|
||||
// }
|
||||
// user := UserEntry{}
|
||||
//
|
||||
// if err := result.Unmarshal(&user); err != nil {
|
||||
// // ...
|
||||
// }
|
||||
func (e *Entry) Unmarshal(i interface{}) (err error) {
|
||||
// Make sure it's a ptr
|
||||
if vo := reflect.ValueOf(i).Kind(); vo != reflect.Ptr {
|
||||
return fmt.Errorf("ldap: cannot use %s, expected pointer to a struct", vo)
|
||||
}
|
||||
|
||||
sv, st := reflect.ValueOf(i).Elem(), reflect.TypeOf(i).Elem()
|
||||
// Make sure it's pointing to a struct
|
||||
if sv.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("ldap: expected pointer to a struct, got %s", sv.Kind())
|
||||
}
|
||||
|
||||
for n := 0; n < st.NumField(); n++ {
|
||||
// Holds struct field value and type
|
||||
fv, ft := sv.Field(n), st.Field(n)
|
||||
|
||||
// skip unexported fields
|
||||
if ft.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// omitempty can be safely discarded, as it's not needed when unmarshalling
|
||||
fieldTag, _ := readTag(ft)
|
||||
|
||||
// Fill the field with the distinguishedName if the tag key is `dn`
|
||||
if fieldTag == "dn" {
|
||||
fv.SetString(e.DN)
|
||||
continue
|
||||
}
|
||||
|
||||
values := e.GetAttributeValues(fieldTag)
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch fv.Interface().(type) {
|
||||
case []string:
|
||||
for _, item := range values {
|
||||
fv.Set(reflect.Append(fv, reflect.ValueOf(item)))
|
||||
}
|
||||
case string:
|
||||
fv.SetString(values[0])
|
||||
case []byte:
|
||||
fv.SetBytes([]byte(values[0]))
|
||||
case int, int64:
|
||||
intVal, err := strconv.ParseInt(values[0], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap: could not parse value '%s' into int field", values[0])
|
||||
}
|
||||
fv.SetInt(intVal)
|
||||
case time.Time:
|
||||
t, err := ber.ParseGeneralizedTime([]byte(values[0]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap: could not parse value '%s' into time.Time field", values[0])
|
||||
}
|
||||
fv.Set(reflect.ValueOf(t))
|
||||
case *DN:
|
||||
dn, err := ParseDN(values[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap: could not parse value '%s' into *ldap.DN field", values[0])
|
||||
}
|
||||
fv.Set(reflect.ValueOf(dn))
|
||||
case []*DN:
|
||||
for _, item := range values {
|
||||
dn, err := ParseDN(item)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap: could not parse value '%s' into *ldap.DN field", item)
|
||||
}
|
||||
fv.Set(reflect.Append(fv, reflect.ValueOf(dn)))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("ldap: expected field to be of type string, []string, int, int64, []byte, *DN, []*DN or time.Time, got %v", ft.Type)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
|
||||
func NewEntryAttribute(name string, values []string) *EntryAttribute {
|
||||
var bytes [][]byte
|
||||
|
@ -218,6 +371,35 @@ func (s *SearchResult) PrettyPrint(indent int) {
|
|||
}
|
||||
}
|
||||
|
||||
// appendTo appends all entries of `s` to `r`
|
||||
func (s *SearchResult) appendTo(r *SearchResult) {
|
||||
r.Entries = append(r.Entries, s.Entries...)
|
||||
r.Referrals = append(r.Referrals, s.Referrals...)
|
||||
r.Controls = append(r.Controls, s.Controls...)
|
||||
}
|
||||
|
||||
// SearchSingleResult holds the server's single entry response to a search request
|
||||
type SearchSingleResult struct {
|
||||
// Entry is the returned entry
|
||||
Entry *Entry
|
||||
// Referral is the returned referral
|
||||
Referral string
|
||||
// Controls are the returned controls
|
||||
Controls []Control
|
||||
// Error is set when the search request was failed
|
||||
Error error
|
||||
}
|
||||
|
||||
// Print outputs a human-readable description
|
||||
func (s *SearchSingleResult) Print() {
|
||||
s.Entry.Print()
|
||||
}
|
||||
|
||||
// PrettyPrint outputs a human-readable description with indenting
|
||||
func (s *SearchSingleResult) PrettyPrint(indent int) {
|
||||
s.Entry.PrettyPrint(indent)
|
||||
}
|
||||
|
||||
// SearchRequest represents a search request to send to the server
|
||||
type SearchRequest struct {
|
||||
BaseDN string
|
||||
|
@ -285,10 +467,11 @@ func NewSearchRequest(
|
|||
// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
|
||||
// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
|
||||
// The following four cases are possible given the arguments:
|
||||
// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
|
||||
// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
|
||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
|
||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
|
||||
// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
|
||||
// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
|
||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
|
||||
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
|
||||
//
|
||||
// A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
|
||||
func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
|
||||
var pagingControl *ControlPaging
|
||||
|
@ -311,23 +494,19 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32)
|
|||
searchResult := new(SearchResult)
|
||||
for {
|
||||
result, err := l.Search(searchRequest)
|
||||
l.Debug.Printf("Looking for Paging Control...")
|
||||
if result != nil {
|
||||
result.appendTo(searchResult)
|
||||
} else {
|
||||
if err == nil {
|
||||
// We have to do this beautifulness in case something absolutely strange happens, which
|
||||
// should only occur in case there is no packet, but also no error.
|
||||
return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// If an error occurred, all results that have been received so far will be returned
|
||||
return searchResult, err
|
||||
}
|
||||
if result == nil {
|
||||
return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
|
||||
}
|
||||
|
||||
for _, entry := range result.Entries {
|
||||
searchResult.Entries = append(searchResult.Entries, entry)
|
||||
}
|
||||
for _, referral := range result.Referrals {
|
||||
searchResult.Referrals = append(searchResult.Referrals, referral)
|
||||
}
|
||||
for _, control := range result.Controls {
|
||||
searchResult.Controls = append(searchResult.Controls, control)
|
||||
}
|
||||
|
||||
l.Debug.Printf("Looking for Paging Control...")
|
||||
pagingResult := FindControl(result.Controls, ControlTypePaging)
|
||||
|
@ -349,7 +528,9 @@ func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32)
|
|||
if pagingControl != nil {
|
||||
l.Debug.Printf("Abandoning Paging...")
|
||||
pagingControl.PagingSize = 0
|
||||
l.Search(searchRequest)
|
||||
if _, err := l.Search(searchRequest); err != nil {
|
||||
return searchResult, err
|
||||
}
|
||||
}
|
||||
|
||||
return searchResult, nil
|
||||
|
@ -366,7 +547,8 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
|
|||
result := &SearchResult{
|
||||
Entries: make([]*Entry, 0),
|
||||
Referrals: make([]string, 0),
|
||||
Controls: make([]Control, 0)}
|
||||
Controls: make([]Control, 0),
|
||||
}
|
||||
|
||||
for {
|
||||
packet, err := l.readPacket(msgCtx)
|
||||
|
@ -402,6 +584,32 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// SearchAsync performs a search request and returns all search results asynchronously.
|
||||
// This means you get all results until an error happens (or the search successfully finished),
|
||||
// e.g. for size / time limited requests all are recieved until the limit is reached.
|
||||
// To stop the search, call cancel function of the context.
|
||||
func (l *Conn) SearchAsync(
|
||||
ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response {
|
||||
r := newSearchResponse(l, bufferSize)
|
||||
r.start(ctx, searchRequest)
|
||||
return r
|
||||
}
|
||||
|
||||
// Syncrepl is a short name for LDAP Sync Replication engine that works on the
|
||||
// consumer-side. This can perform a persistent search and returns an entry
|
||||
// when the entry is updated on the server side.
|
||||
// To stop the search, call cancel function of the context.
|
||||
func (l *Conn) Syncrepl(
|
||||
ctx context.Context, searchRequest *SearchRequest, bufferSize int,
|
||||
mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
|
||||
) Response {
|
||||
control := NewControlSyncRequest(mode, cookie, reloadHint)
|
||||
searchRequest.Controls = append(searchRequest.Controls, control)
|
||||
r := newSearchResponse(l, bufferSize)
|
||||
r.start(ctx, searchRequest)
|
||||
return r
|
||||
}
|
||||
|
||||
// unpackAttributes will extract all given LDAP attributes and it's values
|
||||
// from the ber.Packet
|
||||
func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
|
||||
|
@ -425,3 +633,58 @@ func unpackAttributes(children []*ber.Packet) []*EntryAttribute {
|
|||
|
||||
return entries
|
||||
}
|
||||
|
||||
// DirSync does a Search with dirSync Control.
|
||||
func (l *Conn) DirSync(
|
||||
searchRequest *SearchRequest, flags int64, maxAttrCount int64, cookie []byte,
|
||||
) (*SearchResult, error) {
|
||||
control := FindControl(searchRequest.Controls, ControlTypeDirSync)
|
||||
if control == nil {
|
||||
c := NewRequestControlDirSync(flags, maxAttrCount, cookie)
|
||||
searchRequest.Controls = append(searchRequest.Controls, c)
|
||||
} else {
|
||||
c := control.(*ControlDirSync)
|
||||
if c.Flags != flags {
|
||||
return nil, fmt.Errorf("flags given in search request (%d) conflicts with flags given in search call (%d)", c.Flags, flags)
|
||||
}
|
||||
if c.MaxAttrCount != maxAttrCount {
|
||||
return nil, fmt.Errorf("MaxAttrCnt given in search request (%d) conflicts with maxAttrCount given in search call (%d)", c.MaxAttrCount, maxAttrCount)
|
||||
}
|
||||
}
|
||||
searchResult, err := l.Search(searchRequest)
|
||||
l.Debug.Printf("Looking for result...")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if searchResult == nil {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
|
||||
}
|
||||
|
||||
l.Debug.Printf("Looking for DirSync Control...")
|
||||
resultControl := FindControl(searchResult.Controls, ControlTypeDirSync)
|
||||
if resultControl == nil {
|
||||
l.Debug.Printf("Could not find dirSyncControl control. Breaking...")
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
cookie = resultControl.(*ControlDirSync).Cookie
|
||||
if len(cookie) == 0 {
|
||||
l.Debug.Printf("Could not find cookie. Breaking...")
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
return searchResult, nil
|
||||
}
|
||||
|
||||
// DirSyncDirSyncAsync performs a search request and returns all search results
|
||||
// asynchronously. This is efficient when the server returns lots of entries.
|
||||
func (l *Conn) DirSyncAsync(
|
||||
ctx context.Context, searchRequest *SearchRequest, bufferSize int,
|
||||
flags, maxAttrCount int64, cookie []byte,
|
||||
) Response {
|
||||
control := NewRequestControlDirSync(flags, maxAttrCount, cookie)
|
||||
searchRequest.Controls = append(searchRequest.Controls, control)
|
||||
r := newSearchResponse(l, bufferSize)
|
||||
r.start(ctx, searchRequest)
|
||||
return r
|
||||
}
|
||||
|
|
1
vendor/github.com/go-ldap/ldap/v3/unbind.go
generated
vendored
1
vendor/github.com/go-ldap/ldap/v3/unbind.go
generated
vendored
|
@ -6,6 +6,7 @@ import (
|
|||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
)
|
||||
|
||||
// ErrConnUnbound is returned when Unbind is called on an already closing connection.
|
||||
var ErrConnUnbound = NewError(ErrorNetwork, errors.New("ldap: connection is closed"))
|
||||
|
||||
type unbindRequest struct{}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue