github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/ldap.v2/search.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  //
     5  // File contains Search functionality
     6  //
     7  // https://tools.ietf.org/html/rfc4511
     8  //
     9  //         SearchRequest ::= [APPLICATION 3] SEQUENCE {
    10  //              baseObject      LDAPDN,
    11  //              scope           ENUMERATED {
    12  //                   baseObject              (0),
    13  //                   singleLevel             (1),
    14  //                   wholeSubtree            (2),
    15  //                   ...  },
    16  //              derefAliases    ENUMERATED {
    17  //                   neverDerefAliases       (0),
    18  //                   derefInSearching        (1),
    19  //                   derefFindingBaseObj     (2),
    20  //                   derefAlways             (3) },
    21  //              sizeLimit       INTEGER (0 ..  maxInt),
    22  //              timeLimit       INTEGER (0 ..  maxInt),
    23  //              typesOnly       BOOLEAN,
    24  //              filter          Filter,
    25  //              attributes      AttributeSelection }
    26  //
    27  //         AttributeSelection ::= SEQUENCE OF selector LDAPString
    28  //                         -- The LDAPString is constrained to
    29  //                         -- <attributeSelector> in Section 4.5.1.8
    30  //
    31  //         Filter ::= CHOICE {
    32  //              and             [0] SET SIZE (1..MAX) OF filter Filter,
    33  //              or              [1] SET SIZE (1..MAX) OF filter Filter,
    34  //              not             [2] Filter,
    35  //              equalityMatch   [3] AttributeValueAssertion,
    36  //              substrings      [4] SubstringFilter,
    37  //              greaterOrEqual  [5] AttributeValueAssertion,
    38  //              lessOrEqual     [6] AttributeValueAssertion,
    39  //              present         [7] AttributeDescription,
    40  //              approxMatch     [8] AttributeValueAssertion,
    41  //              extensibleMatch [9] MatchingRuleAssertion,
    42  //              ...  }
    43  //
    44  //         SubstringFilter ::= SEQUENCE {
    45  //              type           AttributeDescription,
    46  //              substrings     SEQUENCE SIZE (1..MAX) OF substring CHOICE {
    47  //                   initial [0] AssertionValue,  -- can occur at most once
    48  //                   any     [1] AssertionValue,
    49  //                   final   [2] AssertionValue } -- can occur at most once
    50  //              }
    51  //
    52  //         MatchingRuleAssertion ::= SEQUENCE {
    53  //              matchingRule    [1] MatchingRuleId OPTIONAL,
    54  //              type            [2] AttributeDescription OPTIONAL,
    55  //              matchValue      [3] AssertionValue,
    56  //              dnAttributes    [4] BOOLEAN DEFAULT FALSE }
    57  //
    58  //
    59  
    60  package ldap
    61  
    62  import (
    63  	"errors"
    64  	"fmt"
    65  	"sort"
    66  	"strings"
    67  
    68  	"gopkg.in/asn1-ber.v1"
    69  )
    70  
    71  // scope choices
    72  const (
    73  	ScopeBaseObject   = 0
    74  	ScopeSingleLevel  = 1
    75  	ScopeWholeSubtree = 2
    76  )
    77  
    78  // ScopeMap contains human readable descriptions of scope choices
    79  var ScopeMap = map[int]string{
    80  	ScopeBaseObject:   "Base Object",
    81  	ScopeSingleLevel:  "Single Level",
    82  	ScopeWholeSubtree: "Whole Subtree",
    83  }
    84  
    85  // derefAliases
    86  const (
    87  	NeverDerefAliases   = 0
    88  	DerefInSearching    = 1
    89  	DerefFindingBaseObj = 2
    90  	DerefAlways         = 3
    91  )
    92  
    93  // DerefMap contains human readable descriptions of derefAliases choices
    94  var DerefMap = map[int]string{
    95  	NeverDerefAliases:   "NeverDerefAliases",
    96  	DerefInSearching:    "DerefInSearching",
    97  	DerefFindingBaseObj: "DerefFindingBaseObj",
    98  	DerefAlways:         "DerefAlways",
    99  }
   100  
   101  // NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
   102  // The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
   103  // same input map of attributes, the output entry will contain the same order of attributes
   104  func NewEntry(dn string, attributes map[string][]string) *Entry {
   105  	var attributeNames []string
   106  	for attributeName := range attributes {
   107  		attributeNames = append(attributeNames, attributeName)
   108  	}
   109  	sort.Strings(attributeNames)
   110  
   111  	var encodedAttributes []*EntryAttribute
   112  	for _, attributeName := range attributeNames {
   113  		encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
   114  	}
   115  	return &Entry{
   116  		DN:         dn,
   117  		Attributes: encodedAttributes,
   118  	}
   119  }
   120  
   121  // Entry represents a single search result entry
   122  type Entry struct {
   123  	// DN is the distinguished name of the entry
   124  	DN string
   125  	// Attributes are the returned attributes for the entry
   126  	Attributes []*EntryAttribute
   127  }
   128  
   129  // GetAttributeValues returns the values for the named attribute, or an empty list
   130  func (e *Entry) GetAttributeValues(attribute string) []string {
   131  	for _, attr := range e.Attributes {
   132  		if attr.Name == attribute {
   133  			return attr.Values
   134  		}
   135  	}
   136  	return []string{}
   137  }
   138  
   139  // GetRawAttributeValues returns the byte values for the named attribute, or an empty list
   140  func (e *Entry) GetRawAttributeValues(attribute string) [][]byte {
   141  	for _, attr := range e.Attributes {
   142  		if attr.Name == attribute {
   143  			return attr.ByteValues
   144  		}
   145  	}
   146  	return [][]byte{}
   147  }
   148  
   149  // GetAttributeValue returns the first value for the named attribute, or ""
   150  func (e *Entry) GetAttributeValue(attribute string) string {
   151  	values := e.GetAttributeValues(attribute)
   152  	if len(values) == 0 {
   153  		return ""
   154  	}
   155  	return values[0]
   156  }
   157  
   158  // GetRawAttributeValue returns the first value for the named attribute, or an empty slice
   159  func (e *Entry) GetRawAttributeValue(attribute string) []byte {
   160  	values := e.GetRawAttributeValues(attribute)
   161  	if len(values) == 0 {
   162  		return []byte{}
   163  	}
   164  	return values[0]
   165  }
   166  
   167  // Print outputs a human-readable description
   168  func (e *Entry) Print() {
   169  	fmt.Printf("DN: %s\n", e.DN)
   170  	for _, attr := range e.Attributes {
   171  		attr.Print()
   172  	}
   173  }
   174  
   175  // PrettyPrint outputs a human-readable description indenting
   176  func (e *Entry) PrettyPrint(indent int) {
   177  	fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN)
   178  	for _, attr := range e.Attributes {
   179  		attr.PrettyPrint(indent + 2)
   180  	}
   181  }
   182  
   183  // NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
   184  func NewEntryAttribute(name string, values []string) *EntryAttribute {
   185  	var bytes [][]byte
   186  	for _, value := range values {
   187  		bytes = append(bytes, []byte(value))
   188  	}
   189  	return &EntryAttribute{
   190  		Name:       name,
   191  		Values:     values,
   192  		ByteValues: bytes,
   193  	}
   194  }
   195  
   196  // EntryAttribute holds a single attribute
   197  type EntryAttribute struct {
   198  	// Name is the name of the attribute
   199  	Name string
   200  	// Values contain the string values of the attribute
   201  	Values []string
   202  	// ByteValues contain the raw values of the attribute
   203  	ByteValues [][]byte
   204  }
   205  
   206  // Print outputs a human-readable description
   207  func (e *EntryAttribute) Print() {
   208  	fmt.Printf("%s: %s\n", e.Name, e.Values)
   209  }
   210  
   211  // PrettyPrint outputs a human-readable description with indenting
   212  func (e *EntryAttribute) PrettyPrint(indent int) {
   213  	fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values)
   214  }
   215  
   216  // SearchResult holds the server's response to a search request
   217  type SearchResult struct {
   218  	// Entries are the returned entries
   219  	Entries []*Entry
   220  	// Referrals are the returned referrals
   221  	Referrals []string
   222  	// Controls are the returned controls
   223  	Controls []Control
   224  }
   225  
   226  // Print outputs a human-readable description
   227  func (s *SearchResult) Print() {
   228  	for _, entry := range s.Entries {
   229  		entry.Print()
   230  	}
   231  }
   232  
   233  // PrettyPrint outputs a human-readable description with indenting
   234  func (s *SearchResult) PrettyPrint(indent int) {
   235  	for _, entry := range s.Entries {
   236  		entry.PrettyPrint(indent)
   237  	}
   238  }
   239  
   240  // SearchRequest represents a search request to send to the server
   241  type SearchRequest struct {
   242  	BaseDN       string
   243  	Scope        int
   244  	DerefAliases int
   245  	SizeLimit    int
   246  	TimeLimit    int
   247  	TypesOnly    bool
   248  	Filter       string
   249  	Attributes   []string
   250  	Controls     []Control
   251  }
   252  
   253  func (s *SearchRequest) encode() (*ber.Packet, error) {
   254  	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request")
   255  	request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN"))
   256  	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope"))
   257  	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases"))
   258  	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit"))
   259  	request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit"))
   260  	request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only"))
   261  	// compile and encode filter
   262  	filterPacket, err := CompileFilter(s.Filter)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	request.AppendChild(filterPacket)
   267  	// encode attributes
   268  	attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
   269  	for _, attribute := range s.Attributes {
   270  		attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
   271  	}
   272  	request.AppendChild(attributesPacket)
   273  	return request, nil
   274  }
   275  
   276  // NewSearchRequest creates a new search request
   277  func NewSearchRequest(
   278  	BaseDN string,
   279  	Scope, DerefAliases, SizeLimit, TimeLimit int,
   280  	TypesOnly bool,
   281  	Filter string,
   282  	Attributes []string,
   283  	Controls []Control,
   284  ) *SearchRequest {
   285  	return &SearchRequest{
   286  		BaseDN:       BaseDN,
   287  		Scope:        Scope,
   288  		DerefAliases: DerefAliases,
   289  		SizeLimit:    SizeLimit,
   290  		TimeLimit:    TimeLimit,
   291  		TypesOnly:    TypesOnly,
   292  		Filter:       Filter,
   293  		Attributes:   Attributes,
   294  		Controls:     Controls,
   295  	}
   296  }
   297  
   298  // SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
   299  // search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
   300  // The following four cases are possible given the arguments:
   301  //  - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
   302  //  - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
   303  //  - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
   304  //  - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
   305  // A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
   306  func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
   307  	var pagingControl *ControlPaging
   308  
   309  	control := FindControl(searchRequest.Controls, ControlTypePaging)
   310  	if control == nil {
   311  		pagingControl = NewControlPaging(pagingSize)
   312  		searchRequest.Controls = append(searchRequest.Controls, pagingControl)
   313  	} else {
   314  		castControl, ok := control.(*ControlPaging)
   315  		if !ok {
   316  			return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control)
   317  		}
   318  		if castControl.PagingSize != pagingSize {
   319  			return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
   320  		}
   321  		pagingControl = castControl
   322  	}
   323  
   324  	searchResult := new(SearchResult)
   325  	for {
   326  		result, err := l.Search(searchRequest)
   327  		l.Debug.Printf("Looking for Paging Control...")
   328  		if err != nil {
   329  			return searchResult, err
   330  		}
   331  		if result == nil {
   332  			return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received"))
   333  		}
   334  
   335  		for _, entry := range result.Entries {
   336  			searchResult.Entries = append(searchResult.Entries, entry)
   337  		}
   338  		for _, referral := range result.Referrals {
   339  			searchResult.Referrals = append(searchResult.Referrals, referral)
   340  		}
   341  		for _, control := range result.Controls {
   342  			searchResult.Controls = append(searchResult.Controls, control)
   343  		}
   344  
   345  		l.Debug.Printf("Looking for Paging Control...")
   346  		pagingResult := FindControl(result.Controls, ControlTypePaging)
   347  		if pagingResult == nil {
   348  			pagingControl = nil
   349  			l.Debug.Printf("Could not find paging control.  Breaking...")
   350  			break
   351  		}
   352  
   353  		cookie := pagingResult.(*ControlPaging).Cookie
   354  		if len(cookie) == 0 {
   355  			pagingControl = nil
   356  			l.Debug.Printf("Could not find cookie.  Breaking...")
   357  			break
   358  		}
   359  		pagingControl.SetCookie(cookie)
   360  	}
   361  
   362  	if pagingControl != nil {
   363  		l.Debug.Printf("Abandoning Paging...")
   364  		pagingControl.PagingSize = 0
   365  		l.Search(searchRequest)
   366  	}
   367  
   368  	return searchResult, nil
   369  }
   370  
   371  // Search performs the given search request
   372  func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) {
   373  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
   374  	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
   375  	// encode search request
   376  	encodedSearchRequest, err := searchRequest.encode()
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	packet.AppendChild(encodedSearchRequest)
   381  	// encode search controls
   382  	if searchRequest.Controls != nil {
   383  		packet.AppendChild(encodeControls(searchRequest.Controls))
   384  	}
   385  
   386  	l.Debug.PrintPacket(packet)
   387  
   388  	msgCtx, err := l.sendMessage(packet)
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	defer l.finishMessage(msgCtx)
   393  
   394  	result := &SearchResult{
   395  		Entries:   make([]*Entry, 0),
   396  		Referrals: make([]string, 0),
   397  		Controls:  make([]Control, 0)}
   398  
   399  	foundSearchResultDone := false
   400  	for !foundSearchResultDone {
   401  		l.Debug.Printf("%d: waiting for response", msgCtx.id)
   402  		packetResponse, ok := <-msgCtx.responses
   403  		if !ok {
   404  			return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
   405  		}
   406  		packet, err = packetResponse.ReadPacket()
   407  		l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
   408  		if err != nil {
   409  			return nil, err
   410  		}
   411  
   412  		if l.Debug {
   413  			if err := addLDAPDescriptions(packet); err != nil {
   414  				return nil, err
   415  			}
   416  			ber.PrintPacket(packet)
   417  		}
   418  
   419  		switch packet.Children[1].Tag {
   420  		case 4:
   421  			entry := new(Entry)
   422  			entry.DN = packet.Children[1].Children[0].Value.(string)
   423  			for _, child := range packet.Children[1].Children[1].Children {
   424  				attr := new(EntryAttribute)
   425  				attr.Name = child.Children[0].Value.(string)
   426  				for _, value := range child.Children[1].Children {
   427  					attr.Values = append(attr.Values, value.Value.(string))
   428  					attr.ByteValues = append(attr.ByteValues, value.ByteValue)
   429  				}
   430  				entry.Attributes = append(entry.Attributes, attr)
   431  			}
   432  			result.Entries = append(result.Entries, entry)
   433  		case 5:
   434  			resultCode, resultDescription := getLDAPResultCode(packet)
   435  			if resultCode != 0 {
   436  				return result, NewError(resultCode, errors.New(resultDescription))
   437  			}
   438  			if len(packet.Children) == 3 {
   439  				for _, child := range packet.Children[2].Children {
   440  					result.Controls = append(result.Controls, DecodeControl(child))
   441  				}
   442  			}
   443  			foundSearchResultDone = true
   444  		case 19:
   445  			result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string))
   446  		}
   447  	}
   448  	l.Debug.Printf("%d: returning", msgCtx.id)
   449  	return result, nil
   450  }