| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 | 
							- // Copyright 2011 The Go Authors. All rights reserved.
 
- // Use of this source code is governed by a BSD-style
 
- // license that can be found in the LICENSE file.
 
- //
 
- // File contains Search functionality
 
- //
 
- // https://tools.ietf.org/html/rfc4511
 
- //
 
- //         SearchRequest ::= [APPLICATION 3] SEQUENCE {
 
- //              baseObject      LDAPDN,
 
- //              scope           ENUMERATED {
 
- //                   baseObject              (0),
 
- //                   singleLevel             (1),
 
- //                   wholeSubtree            (2),
 
- //                   ...  },
 
- //              derefAliases    ENUMERATED {
 
- //                   neverDerefAliases       (0),
 
- //                   derefInSearching        (1),
 
- //                   derefFindingBaseObj     (2),
 
- //                   derefAlways             (3) },
 
- //              sizeLimit       INTEGER (0 ..  maxInt),
 
- //              timeLimit       INTEGER (0 ..  maxInt),
 
- //              typesOnly       BOOLEAN,
 
- //              filter          Filter,
 
- //              attributes      AttributeSelection }
 
- //
 
- //         AttributeSelection ::= SEQUENCE OF selector LDAPString
 
- //                         -- The LDAPString is constrained to
 
- //                         -- <attributeSelector> in Section 4.5.1.8
 
- //
 
- //         Filter ::= CHOICE {
 
- //              and             [0] SET SIZE (1..MAX) OF filter Filter,
 
- //              or              [1] SET SIZE (1..MAX) OF filter Filter,
 
- //              not             [2] Filter,
 
- //              equalityMatch   [3] AttributeValueAssertion,
 
- //              substrings      [4] SubstringFilter,
 
- //              greaterOrEqual  [5] AttributeValueAssertion,
 
- //              lessOrEqual     [6] AttributeValueAssertion,
 
- //              present         [7] AttributeDescription,
 
- //              approxMatch     [8] AttributeValueAssertion,
 
- //              extensibleMatch [9] MatchingRuleAssertion,
 
- //              ...  }
 
- //
 
- //         SubstringFilter ::= SEQUENCE {
 
- //              type           AttributeDescription,
 
- //              substrings     SEQUENCE SIZE (1..MAX) OF substring CHOICE {
 
- //                   initial [0] AssertionValue,  -- can occur at most once
 
- //                   any     [1] AssertionValue,
 
- //                   final   [2] AssertionValue } -- can occur at most once
 
- //              }
 
- //
 
- //         MatchingRuleAssertion ::= SEQUENCE {
 
- //              matchingRule    [1] MatchingRuleId OPTIONAL,
 
- //              type            [2] AttributeDescription OPTIONAL,
 
- //              matchValue      [3] AssertionValue,
 
- //              dnAttributes    [4] BOOLEAN DEFAULT FALSE }
 
- //
 
- //
 
- package ldap
 
- import (
 
- 	"errors"
 
- 	"fmt"
 
- 	"sort"
 
- 	"strings"
 
- 	"gopkg.in/asn1-ber.v1"
 
- )
 
- // scope choices
 
- const (
 
- 	ScopeBaseObject   = 0
 
- 	ScopeSingleLevel  = 1
 
- 	ScopeWholeSubtree = 2
 
- )
 
- // ScopeMap contains human readable descriptions of scope choices
 
- var ScopeMap = map[int]string{
 
- 	ScopeBaseObject:   "Base Object",
 
- 	ScopeSingleLevel:  "Single Level",
 
- 	ScopeWholeSubtree: "Whole Subtree",
 
- }
 
- // derefAliases
 
- const (
 
- 	NeverDerefAliases   = 0
 
- 	DerefInSearching    = 1
 
- 	DerefFindingBaseObj = 2
 
- 	DerefAlways         = 3
 
- )
 
- // DerefMap contains human readable descriptions of derefAliases choices
 
- var DerefMap = map[int]string{
 
- 	NeverDerefAliases:   "NeverDerefAliases",
 
- 	DerefInSearching:    "DerefInSearching",
 
- 	DerefFindingBaseObj: "DerefFindingBaseObj",
 
- 	DerefAlways:         "DerefAlways",
 
- }
 
- // NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
 
- // The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
 
- // same input map of attributes, the output entry will contain the same order of attributes
 
- func NewEntry(dn string, attributes map[string][]string) *Entry {
 
- 	var attributeNames []string
 
- 	for attributeName := range attributes {
 
- 		attributeNames = append(attributeNames, attributeName)
 
- 	}
 
- 	sort.Strings(attributeNames)
 
- 	var encodedAttributes []*EntryAttribute
 
- 	for _, attributeName := range attributeNames {
 
- 		encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
 
- 	}
 
- 	return &Entry{
 
- 		DN:         dn,
 
- 		Attributes: encodedAttributes,
 
- 	}
 
- }
 
- // Entry represents a single search result entry
 
- type Entry struct {
 
- 	// DN is the distinguished name of the entry
 
- 	DN string
 
- 	// Attributes are the returned attributes for the entry
 
- 	Attributes []*EntryAttribute
 
- }
 
- // GetAttributeValues returns the values for the named attribute, or an empty list
 
- func (e *Entry) GetAttributeValues(attribute string) []string {
 
- 	for _, attr := range e.Attributes {
 
- 		if attr.Name == attribute {
 
- 			return attr.Values
 
- 		}
 
- 	}
 
- 	return []string{}
 
- }
 
- // GetRawAttributeValues returns the byte values for the named attribute, or an empty list
 
- func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
 
- 	for _, attr := range e.Attributes {
 
- 		if attr.Name == attribute {
 
- 			return attr.ByteValues
 
- 		}
 
- 	}
 
- 	return [][]byte{}
 
- }
 
- // GetAttributeValue returns the first value for the named attribute, or ""
 
- func (e *Entry) GetAttributeValue(attribute string) string {
 
- 	values := e.GetAttributeValues(attribute)
 
- 	if len(values) == 0 {
 
- 		return ""
 
- 	}
 
- 	return values[0]
 
- }
 
- // GetRawAttributeValue returns the first value for the named attribute, or an empty slice
 
- func (e *Entry) GetRawAttributeValue(attribute string) []byte {
 
- 	values := e.GetRawAttributeValues(attribute)
 
- 	if len(values) == 0 {
 
- 		return []byte{}
 
- 	}
 
- 	return values[0]
 
- }
 
- // Print outputs a human-readable description
 
- func (e *Entry) Print() {
 
- 	fmt.Printf("DN: %s\n", e.DN)
 
- 	for _, attr := range e.Attributes {
 
- 		attr.Print()
 
- 	}
 
- }
 
- // PrettyPrint outputs a human-readable description indenting
 
- func (e *Entry) PrettyPrint(indent int) {
 
- 	fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
 
- 	for _, attr := range e.Attributes {
 
- 		attr.PrettyPrint(indent + 2)
 
- 	}
 
- }
 
- // NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
 
- func NewEntryAttribute(name string, values []string) *EntryAttribute {
 
- 	var bytes [][]byte
 
- 	for _, value := range values {
 
- 		bytes = append(bytes, []byte(value))
 
- 	}
 
- 	return &EntryAttribute{
 
- 		Name:       name,
 
- 		Values:     values,
 
- 		ByteValues: bytes,
 
- 	}
 
- }
 
- // EntryAttribute holds a single attribute
 
- type EntryAttribute struct {
 
- 	// Name is the name of the attribute
 
- 	Name string
 
- 	// Values contain the string values of the attribute
 
- 	Values []string
 
- 	// ByteValues contain the raw values of the attribute
 
- 	ByteValues [][]byte
 
- }
 
- // Print outputs a human-readable description
 
- func (e *EntryAttribute) Print() {
 
- 	fmt.Printf("%s: %s\n", e.Name, e.Values)
 
- }
 
- // PrettyPrint outputs a human-readable description with indenting
 
- func (e *EntryAttribute) PrettyPrint(indent int) {
 
- 	fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
 
- }
 
- // SearchResult holds the server's response to a search request
 
- type SearchResult struct {
 
- 	// Entries are the returned entries
 
- 	Entries []*Entry
 
- 	// Referrals are the returned referrals
 
- 	Referrals []string
 
- 	// Controls are the returned controls
 
- 	Controls []Control
 
- }
 
- // Print outputs a human-readable description
 
- func (s *SearchResult) Print() {
 
- 	for _, entry := range s.Entries {
 
- 		entry.Print()
 
- 	}
 
- }
 
- // PrettyPrint outputs a human-readable description with indenting
 
- func (s *SearchResult) PrettyPrint(indent int) {
 
- 	for _, entry := range s.Entries {
 
- 		entry.PrettyPrint(indent)
 
- 	}
 
- }
 
- // SearchRequest represents a search request to send to the server
 
- type SearchRequest struct {
 
- 	BaseDN       string
 
- 	Scope        int
 
- 	DerefAliases int
 
- 	SizeLimit    int
 
- 	TimeLimit    int
 
- 	TypesOnly    bool
 
- 	Filter       string
 
- 	Attributes   []string
 
- 	Controls     []Control
 
- }
 
- func (s *SearchRequest) encode() (*ber.Packet, error) {
 
- 	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
 
- 	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN"))
 
- 	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope"))
 
- 	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases"))
 
- 	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit"))
 
- 	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit"))
 
- 	request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only"))
 
- 	// compile and encode filter
 
- 	filterPacket, err := CompileFilter(s.Filter)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	request.AppendChild(filterPacket)
 
- 	// encode attributes
 
- 	attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
 
- 	for _, attribute := range s.Attributes {
 
- 		attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
 
- 	}
 
- 	request.AppendChild(attributesPacket)
 
- 	return request, nil
 
- }
 
- // NewSearchRequest creates a new search request
 
- func NewSearchRequest(
 
- 	BaseDN string,
 
- 	Scope, DerefAliases, SizeLimit, TimeLimit int,
 
- 	TypesOnly bool,
 
- 	Filter string,
 
- 	Attributes []string,
 
- 	Controls []Control,
 
- ) *SearchRequest {
 
- 	return &SearchRequest{
 
- 		BaseDN:       BaseDN,
 
- 		Scope:        Scope,
 
- 		DerefAliases: DerefAliases,
 
- 		SizeLimit:    SizeLimit,
 
- 		TimeLimit:    TimeLimit,
 
- 		TypesOnly:    TypesOnly,
 
- 		Filter:       Filter,
 
- 		Attributes:   Attributes,
 
- 		Controls:     Controls,
 
- 	}
 
- }
 
- // 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
 
- // 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
 
- 	control := FindControl(searchRequest.Controls, ControlTypePaging)
 
- 	if control == nil {
 
- 		pagingControl = NewControlPaging(pagingSize)
 
- 		searchRequest.Controls = append(searchRequest.Controls, pagingControl)
 
- 	} else {
 
- 		castControl, ok := control.(*ControlPaging)
 
- 		if !ok {
 
- 			return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control)
 
- 		}
 
- 		if castControl.PagingSize != pagingSize {
 
- 			return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
 
- 		}
 
- 		pagingControl = castControl
 
- 	}
 
- 	searchResult := new(SearchResult)
 
- 	for {
 
- 		result, err := l.Search(searchRequest)
 
- 		l.Debug.Printf("Looking for Paging Control...")
 
- 		if err != nil {
 
- 			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)
 
- 		if pagingResult == nil {
 
- 			pagingControl = nil
 
- 			l.Debug.Printf("Could not find paging control.  Breaking...")
 
- 			break
 
- 		}
 
- 		cookie := pagingResult.(*ControlPaging).Cookie
 
- 		if len(cookie) == 0 {
 
- 			pagingControl = nil
 
- 			l.Debug.Printf("Could not find cookie.  Breaking...")
 
- 			break
 
- 		}
 
- 		pagingControl.SetCookie(cookie)
 
- 	}
 
- 	if pagingControl != nil {
 
- 		l.Debug.Printf("Abandoning Paging...")
 
- 		pagingControl.PagingSize = 0
 
- 		l.Search(searchRequest)
 
- 	}
 
- 	return searchResult, nil
 
- }
 
- // Search performs the given search request
 
- func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
 
- 	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
 
- 	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
 
- 	// encode search request
 
- 	encodedSearchRequest, err := searchRequest.encode()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	packet.AppendChild(encodedSearchRequest)
 
- 	// encode search controls
 
- 	if searchRequest.Controls != nil {
 
- 		packet.AppendChild(encodeControls(searchRequest.Controls))
 
- 	}
 
- 	l.Debug.PrintPacket(packet)
 
- 	msgCtx, err := l.sendMessage(packet)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	defer l.finishMessage(msgCtx)
 
- 	result := &SearchResult{
 
- 		Entries:   make([]*Entry, 0),
 
- 		Referrals: make([]string, 0),
 
- 		Controls:  make([]Control, 0)}
 
- 	foundSearchResultDone := false
 
- 	for !foundSearchResultDone {
 
- 		l.Debug.Printf("%d: waiting for response", msgCtx.id)
 
- 		packetResponse, ok := <-msgCtx.responses
 
- 		if !ok {
 
- 			return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
 
- 		}
 
- 		packet, err = packetResponse.ReadPacket()
 
- 		l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
 
- 		if err != nil {
 
- 			return nil, err
 
- 		}
 
- 		if l.Debug {
 
- 			if err := addLDAPDescriptions(packet); err != nil {
 
- 				return nil, err
 
- 			}
 
- 			ber.PrintPacket(packet)
 
- 		}
 
- 		switch packet.Children[1].Tag {
 
- 		case 4:
 
- 			entry := new(Entry)
 
- 			entry.DN = packet.Children[1].Children[0].Value.(string)
 
- 			for _, child := range packet.Children[1].Children[1].Children {
 
- 				attr := new(EntryAttribute)
 
- 				attr.Name = child.Children[0].Value.(string)
 
- 				for _, value := range child.Children[1].Children {
 
- 					attr.Values = append(attr.Values, value.Value.(string))
 
- 					attr.ByteValues = append(attr.ByteValues, value.ByteValue)
 
- 				}
 
- 				entry.Attributes = append(entry.Attributes, attr)
 
- 			}
 
- 			result.Entries = append(result.Entries, entry)
 
- 		case 5:
 
- 			resultCode, resultDescription := getLDAPResultCode(packet)
 
- 			if resultCode != 0 {
 
- 				return result, NewError(resultCode, errors.New(resultDescription))
 
- 			}
 
- 			if len(packet.Children) == 3 {
 
- 				for _, child := range packet.Children[2].Children {
 
- 					result.Controls = append(result.Controls, DecodeControl(child))
 
- 				}
 
- 			}
 
- 			foundSearchResultDone = true
 
- 		case 19:
 
- 			result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
 
- 		}
 
- 	}
 
- 	l.Debug.Printf("%d: returning", msgCtx.id)
 
- 	return result, nil
 
- }
 
 
  |