-
Notifications
You must be signed in to change notification settings - Fork 365
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Versioned v3 according to Go wiki, maintaining backward compatibility (…
…#247) * Moved v3 to subfolder to allow for versioning >2 with go modules. Reverted top level go.mod to fix backward compatibility * Updated readme to include directions on Go Modules, including the rationale
- Loading branch information
1 parent
3c7ebc5
commit ec72334
Showing
30 changed files
with
5,489 additions
and
1 deletion.
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
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 @@ | ||
// | ||
// https://tools.ietf.org/html/rfc4511 | ||
// | ||
// AddRequest ::= [APPLICATION 8] SEQUENCE { | ||
// entry LDAPDN, | ||
// attributes AttributeList } | ||
// | ||
// AttributeList ::= SEQUENCE OF attribute Attribute | ||
|
||
package ldap | ||
|
||
import ( | ||
"log" | ||
|
||
ber "github.com/go-asn1-ber/asn1-ber" | ||
) | ||
|
||
// Attribute represents an LDAP attribute | ||
type Attribute struct { | ||
// Type is the name of the LDAP attribute | ||
Type string | ||
// Vals are the LDAP attribute values | ||
Vals []string | ||
} | ||
|
||
func (a *Attribute) encode() *ber.Packet { | ||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute") | ||
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type")) | ||
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") | ||
for _, value := range a.Vals { | ||
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) | ||
} | ||
seq.AppendChild(set) | ||
return seq | ||
} | ||
|
||
// AddRequest represents an LDAP AddRequest operation | ||
type AddRequest struct { | ||
// DN identifies the entry being added | ||
DN string | ||
// Attributes list the attributes of the new entry | ||
Attributes []Attribute | ||
// Controls hold optional controls to send with the request | ||
Controls []Control | ||
} | ||
|
||
func (req *AddRequest) appendTo(envelope *ber.Packet) error { | ||
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") | ||
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) | ||
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") | ||
for _, attribute := range req.Attributes { | ||
attributes.AppendChild(attribute.encode()) | ||
} | ||
pkt.AppendChild(attributes) | ||
|
||
envelope.AppendChild(pkt) | ||
if len(req.Controls) > 0 { | ||
envelope.AppendChild(encodeControls(req.Controls)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Attribute adds an attribute with the given type and values | ||
func (req *AddRequest) Attribute(attrType string, attrVals []string) { | ||
req.Attributes = append(req.Attributes, Attribute{Type: attrType, Vals: attrVals}) | ||
} | ||
|
||
// NewAddRequest returns an AddRequest for the given DN, with no attributes | ||
func NewAddRequest(dn string, controls []Control) *AddRequest { | ||
return &AddRequest{ | ||
DN: dn, | ||
Controls: controls, | ||
} | ||
|
||
} | ||
|
||
// Add performs the given AddRequest | ||
func (l *Conn) Add(addRequest *AddRequest) error { | ||
msgCtx, err := l.doRequest(addRequest) | ||
if err != nil { | ||
return err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if packet.Children[1].Tag == ApplicationAddResponse { | ||
err := GetLDAPError(packet) | ||
if err != nil { | ||
return err | ||
} | ||
} else { | ||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag) | ||
} | ||
return 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,152 @@ | ||
package ldap | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
ber "github.com/go-asn1-ber/asn1-ber" | ||
) | ||
|
||
// SimpleBindRequest represents a username/password bind operation | ||
type SimpleBindRequest struct { | ||
// Username is the name of the Directory object that the client wishes to bind as | ||
Username string | ||
// Password is the credentials to bind with | ||
Password string | ||
// Controls are optional controls to send with the bind request | ||
Controls []Control | ||
// AllowEmptyPassword sets whether the client allows binding with an empty password | ||
// (normally used for unauthenticated bind). | ||
AllowEmptyPassword bool | ||
} | ||
|
||
// SimpleBindResult contains the response from the server | ||
type SimpleBindResult struct { | ||
Controls []Control | ||
} | ||
|
||
// NewSimpleBindRequest returns a bind request | ||
func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { | ||
return &SimpleBindRequest{ | ||
Username: username, | ||
Password: password, | ||
Controls: controls, | ||
AllowEmptyPassword: false, | ||
} | ||
} | ||
|
||
func (req *SimpleBindRequest) appendTo(envelope *ber.Packet) error { | ||
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") | ||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) | ||
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Username, "User Name")) | ||
pkt.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, req.Password, "Password")) | ||
|
||
envelope.AppendChild(pkt) | ||
if len(req.Controls) > 0 { | ||
envelope.AppendChild(encodeControls(req.Controls)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SimpleBind performs the simple bind operation defined in the given request | ||
func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { | ||
if simpleBindRequest.Password == "" && !simpleBindRequest.AllowEmptyPassword { | ||
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client")) | ||
} | ||
|
||
msgCtx, err := l.doRequest(simpleBindRequest) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
result := &SimpleBindResult{ | ||
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) | ||
} | ||
result.Controls = append(result.Controls, decodedChild) | ||
} | ||
} | ||
|
||
err = GetLDAPError(packet) | ||
return result, err | ||
} | ||
|
||
// Bind performs a bind with the given username and password. | ||
// | ||
// It does not allow unauthenticated bind (i.e. empty password). Use the UnauthenticatedBind method | ||
// for that. | ||
func (l *Conn) Bind(username, password string) error { | ||
req := &SimpleBindRequest{ | ||
Username: username, | ||
Password: password, | ||
AllowEmptyPassword: false, | ||
} | ||
_, err := l.SimpleBind(req) | ||
return err | ||
} | ||
|
||
// UnauthenticatedBind performs an unauthenticated bind. | ||
// | ||
// A username may be provided for trace (e.g. logging) purpose only, but it is normally not | ||
// authenticated or otherwise validated by the LDAP server. | ||
// | ||
// See https://tools.ietf.org/html/rfc4513#section-5.1.2 . | ||
// See https://tools.ietf.org/html/rfc4513#section-6.3.1 . | ||
func (l *Conn) UnauthenticatedBind(username string) error { | ||
req := &SimpleBindRequest{ | ||
Username: username, | ||
Password: "", | ||
AllowEmptyPassword: true, | ||
} | ||
_, err := l.SimpleBind(req) | ||
return err | ||
} | ||
|
||
var externalBindRequest = requestFunc(func(envelope *ber.Packet) error { | ||
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") | ||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) | ||
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "User Name")) | ||
|
||
saslAuth := ber.Encode(ber.ClassContext, ber.TypeConstructed, 3, "", "authentication") | ||
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "EXTERNAL", "SASL Mech")) | ||
saslAuth.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "SASL Cred")) | ||
|
||
pkt.AppendChild(saslAuth) | ||
|
||
envelope.AppendChild(pkt) | ||
|
||
return nil | ||
}) | ||
|
||
// ExternalBind performs SASL/EXTERNAL authentication. | ||
// | ||
// Use ldap.DialURL("ldapi://") to connect to the Unix socket before ExternalBind. | ||
// | ||
// See https://tools.ietf.org/html/rfc4422#appendix-A | ||
func (l *Conn) ExternalBind() error { | ||
msgCtx, err := l.doRequest(externalBindRequest) | ||
if err != nil { | ||
return err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return GetLDAPError(packet) | ||
} |
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,30 @@ | ||
package ldap | ||
|
||
import ( | ||
"crypto/tls" | ||
"time" | ||
) | ||
|
||
// Client knows how to interact with an LDAP server | ||
type Client interface { | ||
Start() | ||
StartTLS(*tls.Config) error | ||
Close() | ||
SetTimeout(time.Duration) | ||
|
||
Bind(username, password string) error | ||
UnauthenticatedBind(username string) error | ||
SimpleBind(*SimpleBindRequest) (*SimpleBindResult, error) | ||
ExternalBind() error | ||
|
||
Add(*AddRequest) error | ||
Del(*DelRequest) error | ||
Modify(*ModifyRequest) error | ||
ModifyDN(*ModifyDNRequest) error | ||
|
||
Compare(dn, attribute, value string) (bool, error) | ||
PasswordModify(*PasswordModifyRequest) (*PasswordModifyResult, error) | ||
|
||
Search(*SearchRequest) (*SearchResult, error) | ||
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) | ||
} |
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,80 @@ | ||
// File contains Compare functionality | ||
// | ||
// https://tools.ietf.org/html/rfc4511 | ||
// | ||
// CompareRequest ::= [APPLICATION 14] SEQUENCE { | ||
// entry LDAPDN, | ||
// ava AttributeValueAssertion } | ||
// | ||
// AttributeValueAssertion ::= SEQUENCE { | ||
// attributeDesc AttributeDescription, | ||
// assertionValue AssertionValue } | ||
// | ||
// AttributeDescription ::= LDAPString | ||
// -- Constrained to <attributedescription> | ||
// -- [RFC4512] | ||
// | ||
// AttributeValue ::= OCTET STRING | ||
// | ||
|
||
package ldap | ||
|
||
import ( | ||
"fmt" | ||
|
||
ber "github.com/go-asn1-ber/asn1-ber" | ||
) | ||
|
||
// CompareRequest represents an LDAP CompareRequest operation. | ||
type CompareRequest struct { | ||
DN string | ||
Attribute string | ||
Value string | ||
} | ||
|
||
func (req *CompareRequest) appendTo(envelope *ber.Packet) error { | ||
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") | ||
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.DN, "DN")) | ||
|
||
ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") | ||
ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Attribute, "AttributeDesc")) | ||
ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, req.Value, "AssertionValue")) | ||
|
||
pkt.AppendChild(ava) | ||
|
||
envelope.AppendChild(pkt) | ||
|
||
return nil | ||
} | ||
|
||
// Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise | ||
// false with any error that occurs if any. | ||
func (l *Conn) Compare(dn, attribute, value string) (bool, error) { | ||
msgCtx, err := l.doRequest(&CompareRequest{ | ||
DN: dn, | ||
Attribute: attribute, | ||
Value: value}) | ||
if err != nil { | ||
return false, err | ||
} | ||
defer l.finishMessage(msgCtx) | ||
|
||
packet, err := l.readPacket(msgCtx) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if packet.Children[1].Tag == ApplicationCompareResponse { | ||
err := GetLDAPError(packet) | ||
|
||
switch { | ||
case IsErrorWithCode(err, LDAPResultCompareTrue): | ||
return true, nil | ||
case IsErrorWithCode(err, LDAPResultCompareFalse): | ||
return false, nil | ||
default: | ||
return false, err | ||
} | ||
} | ||
return false, fmt.Errorf("unexpected Response: %d", packet.Children[1].Tag) | ||
} |
Oops, something went wrong.