git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/eacl/validator.go (about) 1 package eacl 2 3 import ( 4 "bytes" 5 ) 6 7 // Validator is a tool that calculates 8 // the action on a request according 9 // to the extended ACL rule table. 10 type Validator struct{} 11 12 // NewValidator creates and initializes a new Validator using options. 13 func NewValidator() *Validator { 14 return &Validator{} 15 } 16 17 // CalculateAction calculates action on the request according 18 // to its information represented in ValidationUnit. 19 // 20 // The action is calculated according to the application of 21 // eACL table of rules to the request. 22 // 23 // Second return value is true iff the action was produced by a matching entry. 24 // 25 // If no matching table entry is found or some filters are missing, 26 // ActionAllow is returned and the second return value is false. 27 func (v *Validator) CalculateAction(unit *ValidationUnit) (Action, bool) { 28 for _, record := range unit.table.Records() { 29 // check type of operation 30 if record.Operation() != unit.op { 31 continue 32 } 33 34 // check target 35 if !targetMatches(unit, &record) { 36 continue 37 } 38 39 // check headers 40 switch val := matchFilters(unit.hdrSrc, record.Filters()); { 41 case val < 0: 42 // headers of some type could not be composed => allow 43 return ActionAllow, false 44 case val == 0: 45 return record.Action(), true 46 } 47 } 48 49 return ActionAllow, false 50 } 51 52 // returns: 53 // - positive value if no matching header is found for at least one filter; 54 // - zero if at least one suitable header is found for all filters; 55 // - negative value if the headers of at least one filter cannot be obtained. 56 func matchFilters(hdrSrc TypedHeaderSource, filters []Filter) int { 57 matched := 0 58 59 for _, filter := range filters { 60 headers, ok := hdrSrc.HeadersOfType(filter.From()) 61 if !ok { 62 return -1 63 } 64 65 // get headers of filtering type 66 for _, header := range headers { 67 // prevent NPE 68 if header == nil { 69 continue 70 } 71 72 // check header name 73 if header.Key() != filter.Key() { 74 continue 75 } 76 77 // get match function 78 matchFn, ok := mMatchFns[filter.Matcher()] 79 if !ok { 80 continue 81 } 82 83 // check match 84 if !matchFn(header, &filter) { 85 continue 86 } 87 88 // increment match counter 89 matched++ 90 91 break 92 } 93 } 94 95 return len(filters) - matched 96 } 97 98 // returns true if one of ExtendedACLTarget has 99 // suitable target OR suitable public key. 100 func targetMatches(unit *ValidationUnit, record *Record) bool { 101 for _, target := range record.Targets() { 102 // check public key match 103 if pubs := target.BinaryKeys(); len(pubs) != 0 { 104 for _, key := range pubs { 105 if bytes.Equal(key, unit.key) { 106 return true 107 } 108 } 109 continue 110 } 111 112 // check target group match 113 if unit.role == target.Role() { 114 return true 115 } 116 } 117 118 return false 119 } 120 121 // Maps match type to corresponding function. 122 var mMatchFns = map[Match]func(Header, *Filter) bool{ 123 MatchStringEqual: func(header Header, filter *Filter) bool { 124 return header.Value() == filter.Value() 125 }, 126 127 MatchStringNotEqual: func(header Header, filter *Filter) bool { 128 return header.Value() != filter.Value() 129 }, 130 }