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  }