2016-11-03 16:16:01 -06:00
package ldap
import (
"errors"
"fmt"
"sort"
"strings"
2020-10-15 13:27:33 -06:00
ber "github.com/go-asn1-ber/asn1-ber"
2016-11-03 16:16:01 -06:00
)
// 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 { }
}
2020-10-15 13:27:33 -06:00
// GetEqualFoldAttributeValues returns the values for the named attribute, or an
// empty list. Attribute matching is done with strings.EqualFold.
func ( e * Entry ) GetEqualFoldAttributeValues ( attribute string ) [ ] string {
for _ , attr := range e . Attributes {
if strings . EqualFold ( attribute , attr . Name ) {
return attr . Values
}
}
return [ ] string { }
}
2016-11-03 16:16:01 -06:00
// 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 { }
}
2020-10-15 13:27:33 -06:00
// GetEqualFoldRawAttributeValues returns the byte values for the named attribute, or an empty list
func ( e * Entry ) GetEqualFoldRawAttributeValues ( attribute string ) [ ] [ ] byte {
for _ , attr := range e . Attributes {
if strings . EqualFold ( attr . Name , attribute ) {
return attr . ByteValues
}
}
return [ ] [ ] byte { }
}
2016-11-03 16:16:01 -06:00
// 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 ]
}
2020-10-15 13:27:33 -06:00
// GetEqualFoldAttributeValue returns the first value for the named attribute, or "".
// Attribute comparison is done with strings.EqualFold.
func ( e * Entry ) GetEqualFoldAttributeValue ( attribute string ) string {
values := e . GetEqualFoldAttributeValues ( attribute )
if len ( values ) == 0 {
return ""
}
return values [ 0 ]
}
2016-11-03 16:16:01 -06:00
// 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 ]
}
2020-10-15 13:27:33 -06:00
// GetEqualFoldRawAttributeValue returns the first value for the named attribute, or an empty slice
func ( e * Entry ) GetEqualFoldRawAttributeValue ( attribute string ) [ ] byte {
values := e . GetEqualFoldRawAttributeValues ( attribute )
if len ( values ) == 0 {
return [ ] byte { }
}
return values [ 0 ]
}
2016-11-03 16:16:01 -06:00
// 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
}
2020-10-15 13:27:33 -06:00
func ( req * SearchRequest ) appendTo ( envelope * ber . Packet ) error {
pkt := ber . Encode ( ber . ClassApplication , ber . TypeConstructed , ApplicationSearchRequest , nil , "Search Request" )
pkt . AppendChild ( ber . NewString ( ber . ClassUniversal , ber . TypePrimitive , ber . TagOctetString , req . BaseDN , "Base DN" ) )
pkt . AppendChild ( ber . NewInteger ( ber . ClassUniversal , ber . TypePrimitive , ber . TagEnumerated , uint64 ( req . Scope ) , "Scope" ) )
pkt . AppendChild ( ber . NewInteger ( ber . ClassUniversal , ber . TypePrimitive , ber . TagEnumerated , uint64 ( req . DerefAliases ) , "Deref Aliases" ) )
pkt . AppendChild ( ber . NewInteger ( ber . ClassUniversal , ber . TypePrimitive , ber . TagInteger , uint64 ( req . SizeLimit ) , "Size Limit" ) )
pkt . AppendChild ( ber . NewInteger ( ber . ClassUniversal , ber . TypePrimitive , ber . TagInteger , uint64 ( req . TimeLimit ) , "Time Limit" ) )
pkt . AppendChild ( ber . NewBoolean ( ber . ClassUniversal , ber . TypePrimitive , ber . TagBoolean , req . TypesOnly , "Types Only" ) )
2016-11-03 16:16:01 -06:00
// compile and encode filter
2020-10-15 13:27:33 -06:00
filterPacket , err := CompileFilter ( req . Filter )
2016-11-03 16:16:01 -06:00
if err != nil {
2020-10-15 13:27:33 -06:00
return err
2016-11-03 16:16:01 -06:00
}
2020-10-15 13:27:33 -06:00
pkt . AppendChild ( filterPacket )
2016-11-03 16:16:01 -06:00
// encode attributes
attributesPacket := ber . Encode ( ber . ClassUniversal , ber . TypeConstructed , ber . TagSequence , nil , "Attributes" )
2020-10-15 13:27:33 -06:00
for _ , attribute := range req . Attributes {
2016-11-03 16:16:01 -06:00
attributesPacket . AppendChild ( ber . NewString ( ber . ClassUniversal , ber . TypePrimitive , ber . TagOctetString , attribute , "Attribute" ) )
}
2020-10-15 13:27:33 -06:00
pkt . AppendChild ( attributesPacket )
envelope . AppendChild ( pkt )
if len ( req . Controls ) > 0 {
envelope . AppendChild ( encodeControls ( req . Controls ) )
}
return nil
2016-11-03 16:16:01 -06:00
}
// 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 {
2019-02-18 05:34:37 -07:00
return nil , fmt . Errorf ( "expected paging control to be of type *ControlPaging, got %v" , control )
2016-11-03 16:16:01 -06:00
}
if castControl . PagingSize != pagingSize {
2019-02-18 05:34:37 -07:00
return nil , fmt . Errorf ( "paging size given in search request (%d) conflicts with size given in search call (%d)" , castControl . PagingSize , pagingSize )
2016-11-03 16:16:01 -06:00
}
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 ) {
2020-10-15 13:27:33 -06:00
msgCtx , err := l . doRequest ( searchRequest )
2016-11-03 16:16:01 -06:00
if err != nil {
return nil , err
}
defer l . finishMessage ( msgCtx )
result := & SearchResult {
Entries : make ( [ ] * Entry , 0 ) ,
Referrals : make ( [ ] string , 0 ) ,
Controls : make ( [ ] Control , 0 ) }
2020-10-15 13:27:33 -06:00
for {
packet , err := l . readPacket ( msgCtx )
2016-11-03 16:16:01 -06:00
if err != nil {
2020-10-15 13:27:33 -06:00
return result , err
2016-11-03 16:16:01 -06:00
}
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 :
2019-02-18 05:34:37 -07:00
err := GetLDAPError ( packet )
if err != nil {
2020-10-15 13:27:33 -06:00
return result , err
2016-11-03 16:16:01 -06:00
}
if len ( packet . Children ) == 3 {
for _ , child := range packet . Children [ 2 ] . Children {
2019-02-18 05:34:37 -07:00
decodedChild , err := DecodeControl ( child )
if err != nil {
2020-10-15 13:27:33 -06:00
return result , fmt . Errorf ( "failed to decode child control: %s" , err )
2019-02-18 05:34:37 -07:00
}
result . Controls = append ( result . Controls , decodedChild )
2016-11-03 16:16:01 -06:00
}
}
2020-10-15 13:27:33 -06:00
return result , nil
2016-11-03 16:16:01 -06:00
case 19 :
result . Referrals = append ( result . Referrals , packet . Children [ 1 ] . Children [ 0 ] . Value . ( string ) )
}
}
}