-
Notifications
You must be signed in to change notification settings - Fork 361
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Extended request operations (#516)
- Loading branch information
Showing
6 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package ldap | ||
|
||
import ( | ||
"fmt" | ||
ber "github.com/go-asn1-ber/asn1-ber" | ||
) | ||
|
||
// ExtendedRequest represents an extended request to send to the server | ||
// 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 | ||
Controls []Control | ||
} | ||
|
||
// NewExtendedRequest returns a new ExtendedRequest. The value can be | ||
// nil depending on the type of request | ||
func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { | ||
return &ExtendedRequest{ | ||
Name: name, | ||
Value: value, | ||
} | ||
} | ||
|
||
func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { | ||
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 { | ||
pkt.AppendChild(er.Value) | ||
} | ||
envelope.AppendChild(pkt) | ||
if len(er.Controls) > 0 { | ||
envelope.AppendChild(encodeControls(er.Controls)) | ||
} | ||
return nil | ||
} | ||
|
||
// ExtendedResponse represents the response from the directory server | ||
// after sending an extended request | ||
// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 | ||
type ExtendedResponse struct { | ||
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE { | ||
// COMPONENTS OF LDAPResult, | ||
// responseName [10] LDAPOID OPTIONAL, | ||
// responseValue [11] OCTET STRING OPTIONAL } | ||
|
||
Name string | ||
Value *ber.Packet | ||
Controls []Control | ||
} | ||
|
||
// Extended performs an extended request. The resulting | ||
// ExtendedResponse may return a value in the form of a *ber.Packet | ||
func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { | ||
msgCtx, err := l.doRequest(er) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if err = GetLDAPError(packet); err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(packet.Children[1].Children) < 4 { | ||
return nil, fmt.Errorf( | ||
"ldap: malformed extended response: expected 4 children, got %d", | ||
len(packet.Children), | ||
) | ||
} | ||
|
||
response := &ExtendedResponse{ | ||
Name: packet.Children[1].Children[3].Data.String(), | ||
Controls: make([]Control, 0), | ||
} | ||
|
||
if len(packet.Children) == 3 { | ||
for _, child := range packet.Children[2].Children { | ||
decodedChild, decodeErr := DecodeControl(child) | ||
if decodeErr != nil { | ||
return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) | ||
} | ||
response.Controls = append(response.Controls, decodedChild) | ||
} | ||
} | ||
|
||
if len(packet.Children[1].Children) == 5 { | ||
response.Value = packet.Children[1].Children[4] | ||
} | ||
|
||
return response, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <nil> | ||
|
||
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", nil) | ||
_, err = conn.Extended(request) | ||
if err != nil { | ||
t.Errorf("%s failed: %v", t.Name(), err) | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package ldap | ||
|
||
import ( | ||
"fmt" | ||
ber "github.com/go-asn1-ber/asn1-ber" | ||
) | ||
|
||
// ExtendedRequest represents an extended request to send to the server | ||
// 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 | ||
Controls []Control | ||
} | ||
|
||
// NewExtendedRequest returns a new ExtendedRequest. The value can be | ||
// nil depending on the type of request | ||
func NewExtendedRequest(name string, value *ber.Packet) *ExtendedRequest { | ||
return &ExtendedRequest{ | ||
Name: name, | ||
Value: value, | ||
} | ||
} | ||
|
||
func (er ExtendedRequest) appendTo(envelope *ber.Packet) error { | ||
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 { | ||
pkt.AppendChild(er.Value) | ||
} | ||
envelope.AppendChild(pkt) | ||
if len(er.Controls) > 0 { | ||
envelope.AppendChild(encodeControls(er.Controls)) | ||
} | ||
return nil | ||
} | ||
|
||
// ExtendedResponse represents the response from the directory server | ||
// after sending an extended request | ||
// See: https://www.rfc-editor.org/rfc/rfc4511#section-4.12 | ||
type ExtendedResponse struct { | ||
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE { | ||
// COMPONENTS OF LDAPResult, | ||
// responseName [10] LDAPOID OPTIONAL, | ||
// responseValue [11] OCTET STRING OPTIONAL } | ||
|
||
Name string | ||
Value *ber.Packet | ||
Controls []Control | ||
} | ||
|
||
// Extended performs an extended request. The resulting | ||
// ExtendedResponse may return a value in the form of a *ber.Packet | ||
func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { | ||
msgCtx, err := l.doRequest(er) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if err = GetLDAPError(packet); err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(packet.Children[1].Children) < 4 { | ||
return nil, fmt.Errorf( | ||
"ldap: malformed extended response: expected 4 children, got %d", | ||
len(packet.Children), | ||
) | ||
} | ||
|
||
response := &ExtendedResponse{ | ||
Name: packet.Children[1].Children[3].Data.String(), | ||
Controls: make([]Control, 0), | ||
} | ||
|
||
if len(packet.Children) == 3 { | ||
for _, child := range packet.Children[2].Children { | ||
decodedChild, decodeErr := DecodeControl(child) | ||
if decodeErr != nil { | ||
return nil, fmt.Errorf("failed to decode child control: %s", decodeErr) | ||
} | ||
response.Controls = append(response.Controls, decodedChild) | ||
} | ||
} | ||
|
||
if len(packet.Children[1].Children) == 5 { | ||
response.Value = packet.Children[1].Children[4] | ||
} | ||
|
||
return response, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <nil> | ||
|
||
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", nil) | ||
_, err = conn.Extended(request) | ||
if err != nil { | ||
t.Errorf("%s failed: %v", t.Name(), err) | ||
return | ||
} | ||
} |