github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/urisearch/urisearch.go (about) 1 package urisearch 2 3 import ( 4 "go.aporeto.io/enforcerd/trireme-lib/policy" 5 ) 6 7 type node struct { 8 children map[string]*node 9 leaf bool 10 data interface{} 11 } 12 13 // APICache represents an API cache. 14 type APICache struct { 15 methodRoots map[string]*node 16 ID string 17 External bool 18 } 19 20 type scopeRule struct { 21 rule *policy.HTTPRule 22 } 23 24 // NewAPICache creates a new API cache 25 func NewAPICache(rules []*policy.HTTPRule, id string, external bool) *APICache { 26 a := &APICache{ 27 methodRoots: map[string]*node{}, 28 ID: id, 29 External: external, 30 } 31 32 for _, rule := range rules { 33 sc := &scopeRule{ 34 rule: rule, 35 } 36 for _, method := range rule.Methods { 37 if _, ok := a.methodRoots[method]; !ok { 38 a.methodRoots[method] = &node{} 39 } 40 for _, uri := range rule.URIs { 41 insert(a.methodRoots[method], uri, sc) 42 } 43 } 44 } 45 46 return a 47 } 48 49 // FindRule finds a rule in the APICache without validating scopes 50 func (c *APICache) FindRule(verb, uri string) (bool, *policy.HTTPRule) { 51 found, rule := c.Find(verb, uri) 52 if rule == nil { 53 return found, nil 54 } 55 if policyRule, ok := rule.(*scopeRule); ok { 56 return found, policyRule.rule 57 } 58 return false, nil 59 } 60 61 // FindAndMatchScope finds the rule and returns true only if the scope matches 62 // as well. It also returns true of this was a public rule, allowing the callers 63 // to decide how to present the data or potentially what to do if authorization 64 // fails. 65 func (c *APICache) FindAndMatchScope(verb, uri string, attributes []string) (bool, bool) { 66 found, rule := c.Find(verb, uri) 67 if !found || rule == nil { 68 return false, false 69 } 70 policyRule, ok := rule.(*scopeRule) 71 if !ok { 72 return false, false 73 } 74 if policyRule.rule.Public { 75 return true, true 76 } 77 return c.MatchClaims(policyRule.rule.ClaimMatchingRules, attributes), false 78 } 79 80 // MatchClaims receives a set of claim matchibg rules and a set of claims 81 // and returns true of the claims match the rules. 82 func (c *APICache) MatchClaims(rules [][]string, claims []string) bool { 83 84 claimsMap := map[string]struct{}{} 85 for _, claim := range claims { 86 claimsMap[claim] = struct{}{} 87 } 88 89 var matched int 90 for _, clause := range rules { 91 matched = len(clause) 92 for _, claim := range clause { 93 if _, ok := claimsMap[claim]; ok { 94 matched-- 95 } 96 if matched == 0 { 97 return true 98 } 99 } 100 } 101 return false 102 } 103 104 // Find finds a URI in the cache and returns true and the data if found. 105 // If not found it returns false. 106 func (c *APICache) Find(verb, uri string) (bool, interface{}) { 107 root, ok := c.methodRoots[verb] 108 if !ok { 109 return false, nil 110 } 111 return search(root, uri) 112 } 113 114 // parse parses a URI and splits into prefix, suffix 115 func parse(s string) (string, string) { 116 if s == "/" { 117 return s, "" 118 } 119 for i := 1; i < len(s); i++ { 120 if s[i] == '/' { 121 return s[0:i], s[i:] 122 } 123 } 124 125 return s, "" 126 } 127 128 // insert adds an api to the api cache 129 func insert(n *node, api string, data interface{}) { 130 if len(api) == 0 { 131 n.data = data 132 n.leaf = true 133 return 134 } 135 136 prefix, suffix := parse(api) 137 138 // root node or terminal node 139 if prefix == "/" { 140 n.data = data 141 n.leaf = true 142 return 143 } 144 145 if n.children == nil { 146 n.children = map[string]*node{} 147 } 148 149 // If there is no child, add the new child. 150 next, ok := n.children[prefix] 151 if !ok { 152 next = &node{} 153 n.children[prefix] = next 154 } 155 156 insert(next, suffix, data) 157 } 158 159 func search(n *node, api string) (found bool, data interface{}) { 160 161 prefix, suffix := parse(api) 162 163 if prefix == "/" { 164 if n.leaf { 165 return true, n.data 166 } 167 } 168 169 next, foundPrefix := n.children[prefix] 170 // We found either an exact match or a * match 171 if foundPrefix { 172 matchedChildren, data := search(next, suffix) 173 if matchedChildren { 174 return true, data 175 } 176 } 177 178 // If not found, try the ignore operator. 179 next, foundPrefix = n.children["/?"] 180 if foundPrefix { 181 matchedChildren, data := search(next, suffix) 182 if matchedChildren { 183 return true, data 184 } 185 } 186 187 // If not found, try the * operator and ignore the rest of path. 188 next, foundPrefix = n.children["/*"] 189 if foundPrefix { 190 for len(suffix) > 0 { 191 matchedChildren, data := search(next, suffix) 192 if matchedChildren { 193 return true, data 194 } 195 prefix, suffix = parse(suffix) 196 } 197 matchedChildren, data := search(next, "/") 198 if matchedChildren { 199 return true, data 200 } 201 } 202 203 if n.leaf && len(prefix) == 0 { 204 return true, n.data 205 } 206 207 return false, nil 208 }