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 }