diff --git a/client.go b/client.go index 8d103dd..ee473fc 100644 --- a/client.go +++ b/client.go @@ -28,7 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) - Extended(*ExtendedRequest) (*ExtendResponse, error) + Extended(*ExtendedRequest) (*ExtendedResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/extend.go b/extended.go similarity index 79% rename from extend.go rename to extended.go index 524d4eb..17d7628 100644 --- a/extend.go +++ b/extended.go @@ -8,6 +8,10 @@ import ( // ExtendedRequest TODO // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } @@ -20,14 +24,6 @@ func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { } func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { - // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { - // requestName [0] LDAPOID, - // requestValue [1] OCTET STRING OPTIONAL } - // - // Despite the RFC documentation stating otherwise, the requestName field needs to be - // of class application and type EOC, otherwise the directory server will terminate - // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) if er.Value != nil { @@ -37,22 +33,23 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } -type ExtendResponse struct { +type ExtendedResponse struct { + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } -func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { return nil, err } defer l.finishMessage(msgCtx) - // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { - // COMPONENTS OF LDAPResult, - // responseName [10] LDAPOID OPTIONAL, - // responseValue [11] OCTET STRING OPTIONAL } packet, err := l.readPacket(msgCtx) if err != nil { return nil, err @@ -68,9 +65,10 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { ) } - response := new(ExtendResponse) + response := new(ExtendedResponse) response.Name = packet.Children[1].Children[3].Data.String() - if len(packet.Children) == 4 { + + if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4] } diff --git a/extended_test.go b/extended_test.go new file mode 100644 index 0000000..c0cf4e1 --- /dev/null +++ b/extended_test.go @@ -0,0 +1,41 @@ +package ldap + +import ( + "testing" +) + +func TestExtendedRequest_WhoAmI(t *testing.T) { + l, err := DialURL(ldapServer) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + defer l.Close() + + l.Bind("", "") // anonymous + defer l.Unbind() + + rfc4532req := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) // request value is + + var rfc4532resp *ExtendedResponse + if rfc4532resp, err = l.Extended(rfc4532req); err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + t.Logf("%#v\n", rfc4532resp) +} + +func TestExtendedRequest_FastBind(t *testing.T) { + conn, err := DialURL(ldapServer) + if err != nil { + t.Error(err) + } + defer conn.Close() + + request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + _, err = conn.Extended(request) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } +} diff --git a/v3/client.go b/v3/client.go index 8d103dd..ee473fc 100644 --- a/v3/client.go +++ b/v3/client.go @@ -28,7 +28,7 @@ type Client interface { Modify(*ModifyRequest) error ModifyDN(*ModifyDNRequest) error ModifyWithResult(*ModifyRequest) (*ModifyResult, error) - Extended(*ExtendedRequest) (*ExtendResponse, error) + Extended(*ExtendedRequest) (*ExtendedResponse, error) Compare(dn, attribute, value string) (bool, error) PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) diff --git a/v3/extend.go b/v3/extended.go similarity index 79% rename from v3/extend.go rename to v3/extended.go index 524d4eb..17d7628 100644 --- a/v3/extend.go +++ b/v3/extended.go @@ -8,6 +8,10 @@ import ( // ExtendedRequest TODO // See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 type ExtendedRequest struct { + // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + // requestName [0] LDAPOID, + // requestValue [1] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } @@ -20,14 +24,6 @@ func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { } func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { - // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { - // requestName [0] LDAPOID, - // requestValue [1] OCTET STRING OPTIONAL } - // - // Despite the RFC documentation stating otherwise, the requestName field needs to be - // of class application and type EOC, otherwise the directory server will terminate - // the connection right away (tested against OpenLDAP, Active Directory). - pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Extended Request") pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, ber.TagEOC, er.Name, "Extended Request Name")) if er.Value != nil { @@ -37,22 +33,23 @@ func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { return nil } -type ExtendResponse struct { +type ExtendedResponse struct { + // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + // COMPONENTS OF LDAPResult, + // responseName [10] LDAPOID OPTIONAL, + // responseValue [11] OCTET STRING OPTIONAL } + Name string Value *ber.Packet } -func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { +func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { msgCtx, err := l.doRequest(er) if err != nil { return nil, err } defer l.finishMessage(msgCtx) - // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { - // COMPONENTS OF LDAPResult, - // responseName [10] LDAPOID OPTIONAL, - // responseValue [11] OCTET STRING OPTIONAL } packet, err := l.readPacket(msgCtx) if err != nil { return nil, err @@ -68,9 +65,10 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendResponse, error) { ) } - response := new(ExtendResponse) + response := new(ExtendedResponse) response.Name = packet.Children[1].Children[3].Data.String() - if len(packet.Children) == 4 { + + if len(packet.Children[1].Children) == 5 { response.Value = packet.Children[1].Children[4] } diff --git a/v3/extended_test.go b/v3/extended_test.go new file mode 100644 index 0000000..c0cf4e1 --- /dev/null +++ b/v3/extended_test.go @@ -0,0 +1,41 @@ +package ldap + +import ( + "testing" +) + +func TestExtendedRequest_WhoAmI(t *testing.T) { + l, err := DialURL(ldapServer) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + defer l.Close() + + l.Bind("", "") // anonymous + defer l.Unbind() + + rfc4532req := NewExtendedRequest("1.3.6.1.4.1.4203.1.11.3", nil) // request value is + + var rfc4532resp *ExtendedResponse + if rfc4532resp, err = l.Extended(rfc4532req); err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } + t.Logf("%#v\n", rfc4532resp) +} + +func TestExtendedRequest_FastBind(t *testing.T) { + conn, err := DialURL(ldapServer) + if err != nil { + t.Error(err) + } + defer conn.Close() + + request := NewExtendedRequest(`1.3.6.1.4.1.4203.1.11.3`) + _, err = conn.Extended(request) + if err != nil { + t.Errorf("%s failed: %v", t.Name(), err) + return + } +}