| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- // 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.
- package ldap
- import (
- "errors"
- "fmt"
- "github.com/nmcclain/asn1-ber"
- "strings"
- )
- const (
- FilterAnd = 0
- FilterOr = 1
- FilterNot = 2
- FilterEqualityMatch = 3
- FilterSubstrings = 4
- FilterGreaterOrEqual = 5
- FilterLessOrEqual = 6
- FilterPresent = 7
- FilterApproxMatch = 8
- FilterExtensibleMatch = 9
- )
- var FilterMap = map[uint8]string{
- FilterAnd: "And",
- FilterOr: "Or",
- FilterNot: "Not",
- FilterEqualityMatch: "Equality Match",
- FilterSubstrings: "Substrings",
- FilterGreaterOrEqual: "Greater Or Equal",
- FilterLessOrEqual: "Less Or Equal",
- FilterPresent: "Present",
- FilterApproxMatch: "Approx Match",
- FilterExtensibleMatch: "Extensible Match",
- }
- const (
- FilterSubstringsInitial = 0
- FilterSubstringsAny = 1
- FilterSubstringsFinal = 2
- )
- func CompileFilter(filter string) (*ber.Packet, error) {
- if len(filter) == 0 || filter[0] != '(' {
- return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
- }
- packet, pos, err := compileFilter(filter, 1)
- if err != nil {
- return nil, err
- }
- if pos != len(filter) {
- return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
- }
- return packet, nil
- }
- func DecompileFilter(packet *ber.Packet) (ret string, err error) {
- defer func() {
- if r := recover(); r != nil {
- err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter"))
- }
- }()
- ret = "("
- err = nil
- childStr := ""
- switch packet.Tag {
- case FilterAnd:
- ret += "&"
- for _, child := range packet.Children {
- childStr, err = DecompileFilter(child)
- if err != nil {
- return
- }
- ret += childStr
- }
- case FilterOr:
- ret += "|"
- for _, child := range packet.Children {
- childStr, err = DecompileFilter(child)
- if err != nil {
- return
- }
- ret += childStr
- }
- case FilterNot:
- ret += "!"
- childStr, err = DecompileFilter(packet.Children[0])
- if err != nil {
- return
- }
- ret += childStr
- case FilterSubstrings:
- ret += ber.DecodeString(packet.Children[0].Data.Bytes())
- ret += "="
- switch packet.Children[1].Children[0].Tag {
- case FilterSubstringsInitial:
- ret += ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*"
- case FilterSubstringsAny:
- ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*"
- case FilterSubstringsFinal:
- ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes())
- }
- case FilterEqualityMatch:
- ret += ber.DecodeString(packet.Children[0].Data.Bytes())
- ret += "="
- ret += ber.DecodeString(packet.Children[1].Data.Bytes())
- case FilterGreaterOrEqual:
- ret += ber.DecodeString(packet.Children[0].Data.Bytes())
- ret += ">="
- ret += ber.DecodeString(packet.Children[1].Data.Bytes())
- case FilterLessOrEqual:
- ret += ber.DecodeString(packet.Children[0].Data.Bytes())
- ret += "<="
- ret += ber.DecodeString(packet.Children[1].Data.Bytes())
- case FilterPresent:
- ret += ber.DecodeString(packet.Data.Bytes())
- ret += "=*"
- case FilterApproxMatch:
- ret += ber.DecodeString(packet.Children[0].Data.Bytes())
- ret += "~="
- ret += ber.DecodeString(packet.Children[1].Data.Bytes())
- }
- ret += ")"
- return
- }
- func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
- for pos < len(filter) && filter[pos] == '(' {
- child, newPos, err := compileFilter(filter, pos+1)
- if err != nil {
- return pos, err
- }
- pos = newPos
- parent.AppendChild(child)
- }
- if pos == len(filter) {
- return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
- }
- return pos + 1, nil
- }
- func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
- var packet *ber.Packet
- var err error
- defer func() {
- if r := recover(); r != nil {
- err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
- }
- }()
- newPos := pos
- switch filter[pos] {
- case '(':
- packet, newPos, err = compileFilter(filter, pos+1)
- newPos++
- return packet, newPos, err
- case '&':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
- newPos, err = compileFilterSet(filter, pos+1, packet)
- return packet, newPos, err
- case '|':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
- newPos, err = compileFilterSet(filter, pos+1, packet)
- return packet, newPos, err
- case '!':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
- var child *ber.Packet
- child, newPos, err = compileFilter(filter, pos+1)
- packet.AppendChild(child)
- return packet, newPos, err
- default:
- attribute := ""
- condition := ""
- for newPos < len(filter) && filter[newPos] != ')' {
- switch {
- case packet != nil:
- condition += fmt.Sprintf("%c", filter[newPos])
- case filter[newPos] == '=':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
- case filter[newPos] == '>' && filter[newPos+1] == '=':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
- newPos++
- case filter[newPos] == '<' && filter[newPos+1] == '=':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
- newPos++
- case filter[newPos] == '~' && filter[newPos+1] == '=':
- packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterLessOrEqual])
- newPos++
- case packet == nil:
- attribute += fmt.Sprintf("%c", filter[newPos])
- }
- newPos++
- }
- if newPos == len(filter) {
- err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
- return packet, newPos, err
- }
- if packet == nil {
- err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter"))
- return packet, newPos, err
- }
- // Handle FilterEqualityMatch as a separate case (is primitive, not constructed like the other filters)
- if packet.Tag == FilterEqualityMatch && condition == "*" {
- packet.TagType = ber.TypePrimitive
- packet.Tag = FilterPresent
- packet.Description = FilterMap[packet.Tag]
- packet.Data.WriteString(attribute)
- return packet, newPos + 1, nil
- }
- packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
- switch {
- case packet.Tag == FilterEqualityMatch && condition[0] == '*' && condition[len(condition)-1] == '*':
- // Any
- packet.Tag = FilterSubstrings
- packet.Description = FilterMap[packet.Tag]
- seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
- seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsAny, condition[1:len(condition)-1], "Any Substring"))
- packet.AppendChild(seq)
- case packet.Tag == FilterEqualityMatch && condition[0] == '*':
- // Final
- packet.Tag = FilterSubstrings
- packet.Description = FilterMap[packet.Tag]
- seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
- seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsFinal, condition[1:], "Final Substring"))
- packet.AppendChild(seq)
- case packet.Tag == FilterEqualityMatch && condition[len(condition)-1] == '*':
- // Initial
- packet.Tag = FilterSubstrings
- packet.Description = FilterMap[packet.Tag]
- seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings")
- seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsInitial, condition[:len(condition)-1], "Initial Substring"))
- packet.AppendChild(seq)
- default:
- packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, condition, "Condition"))
- }
- newPos++
- return packet, newPos, err
- }
- }
- func ServerApplyFilter(f *ber.Packet, entry *Entry) (bool, LDAPResultCode) {
- switch FilterMap[f.Tag] {
- default:
- //log.Fatalf("Unknown LDAP filter code: %d", f.Tag)
- return false, LDAPResultOperationsError
- case "Equality Match":
- if len(f.Children) != 2 {
- return false, LDAPResultOperationsError
- }
- attribute := f.Children[0].Value.(string)
- value := f.Children[1].Value.(string)
- for _, a := range entry.Attributes {
- if strings.ToLower(a.Name) == strings.ToLower(attribute) {
- for _, v := range a.Values {
- if strings.ToLower(v) == strings.ToLower(value) {
- return true, LDAPResultSuccess
- }
- }
- }
- }
- case "Present":
- for _, a := range entry.Attributes {
- if strings.ToLower(a.Name) == strings.ToLower(f.Data.String()) {
- return true, LDAPResultSuccess
- }
- }
- case "And":
- for _, child := range f.Children {
- ok, exitCode := ServerApplyFilter(child, entry)
- if exitCode != LDAPResultSuccess {
- return false, exitCode
- }
- if !ok {
- return false, LDAPResultSuccess
- }
- }
- return true, LDAPResultSuccess
- case "Or":
- anyOk := false
- for _, child := range f.Children {
- ok, exitCode := ServerApplyFilter(child, entry)
- if exitCode != LDAPResultSuccess {
- return false, exitCode
- } else if ok {
- anyOk = true
- }
- }
- if anyOk {
- return true, LDAPResultSuccess
- }
- case "Not":
- if len(f.Children) != 1 {
- return false, LDAPResultOperationsError
- }
- ok, exitCode := ServerApplyFilter(f.Children[0], entry)
- if exitCode != LDAPResultSuccess {
- return false, exitCode
- } else if !ok {
- return true, LDAPResultSuccess
- }
- case "Substrings":
- if len(f.Children) != 2 {
- return false, LDAPResultOperationsError
- }
- attribute := f.Children[0].Value.(string)
- bytes := f.Children[1].Children[0].Data.Bytes()
- value := string(bytes[:])
- for _, a := range entry.Attributes {
- if strings.ToLower(a.Name) == strings.ToLower(attribute) {
- for _, v := range a.Values {
- switch f.Children[1].Children[0].Tag {
- case FilterSubstringsInitial:
- if strings.HasPrefix(v, value) {
- return true, LDAPResultSuccess
- }
- case FilterSubstringsAny:
- if strings.Contains(v, value) {
- return true, LDAPResultSuccess
- }
- case FilterSubstringsFinal:
- if strings.HasSuffix(v, value) {
- return true, LDAPResultSuccess
- }
- }
- }
- }
- }
- case "FilterGreaterOrEqual": // TODO
- return false, LDAPResultOperationsError
- case "FilterLessOrEqual": // TODO
- return false, LDAPResultOperationsError
- case "FilterApproxMatch": // TODO
- return false, LDAPResultOperationsError
- case "FilterExtensibleMatch": // TODO
- return false, LDAPResultOperationsError
- }
- return false, LDAPResultSuccess
- }
- func GetFilterObjectClass(filter string) (string, error) {
- f, err := CompileFilter(filter)
- if err != nil {
- return "", err
- }
- return parseFilterObjectClass(f)
- }
- func parseFilterObjectClass(f *ber.Packet) (string, error) {
- objectClass := ""
- switch FilterMap[f.Tag] {
- case "Equality Match":
- if len(f.Children) != 2 {
- return "", errors.New("Equality match must have only two children")
- }
- attribute := strings.ToLower(f.Children[0].Value.(string))
- value := f.Children[1].Value.(string)
- if attribute == "objectclass" {
- objectClass = strings.ToLower(value)
- }
- case "And":
- for _, child := range f.Children {
- subType, err := parseFilterObjectClass(child)
- if err != nil {
- return "", err
- }
- if len(subType) > 0 {
- objectClass = subType
- }
- }
- case "Or":
- for _, child := range f.Children {
- subType, err := parseFilterObjectClass(child)
- if err != nil {
- return "", err
- }
- if len(subType) > 0 {
- objectClass = subType
- }
- }
- case "Not":
- if len(f.Children) != 1 {
- return "", errors.New("Not filter must have only one child")
- }
- subType, err := parseFilterObjectClass(f.Children[0])
- if err != nil {
- return "", err
- }
- if len(subType) > 0 {
- objectClass = subType
- }
- }
- return strings.ToLower(objectClass), nil
- }
|