go-micro.dev/v5@v5.12.0/auth/rules.go (about) 1 package auth 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 ) 8 9 // Verify an account has access to a resource using the rules provided. If the account does not have 10 // access an error will be returned. If there are no rules provided which match the resource, an error 11 // will be returned. 12 func Verify(rules []*Rule, acc *Account, res *Resource) error { 13 // the rule is only to be applied if the type matches the resource or is catch-all (*) 14 validTypes := []string{"*", res.Type} 15 16 // the rule is only to be applied if the name matches the resource or is catch-all (*) 17 validNames := []string{"*", res.Name} 18 19 // rules can have wildcard excludes on endpoints since this can also be a path for web services, 20 // e.g. /foo/* would include /foo/bar. We also want to check for wildcards and the exact endpoint 21 validEndpoints := []string{"*", res.Endpoint} 22 if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 { 23 for i := 1; i < len(comps)+1; i++ { 24 wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/")) 25 validEndpoints = append(validEndpoints, wildcard) 26 } 27 } 28 29 // filter the rules to the ones which match the criteria above 30 filteredRules := make([]*Rule, 0) 31 for _, rule := range rules { 32 if !include(validTypes, rule.Resource.Type) { 33 continue 34 } 35 if !include(validNames, rule.Resource.Name) { 36 continue 37 } 38 if !include(validEndpoints, rule.Resource.Endpoint) { 39 continue 40 } 41 filteredRules = append(filteredRules, rule) 42 } 43 44 // sort the filtered rules by priority, highest to lowest 45 sort.SliceStable(filteredRules, func(i, j int) bool { 46 return filteredRules[i].Priority > filteredRules[j].Priority 47 }) 48 49 // loop through the rules and check for a rule which applies to this account 50 for _, rule := range filteredRules { 51 // a blank scope indicates the rule applies to everyone, even nil accounts 52 if rule.Scope == ScopePublic && rule.Access == AccessDenied { 53 return ErrForbidden 54 } else if rule.Scope == ScopePublic && rule.Access == AccessGranted { 55 return nil 56 } 57 58 // all further checks require an account 59 if acc == nil { 60 continue 61 } 62 63 // this rule applies to any account 64 if rule.Scope == ScopeAccount && rule.Access == AccessDenied { 65 return ErrForbidden 66 } else if rule.Scope == ScopeAccount && rule.Access == AccessGranted { 67 return nil 68 } 69 70 // if the account has the necessary scope 71 if include(acc.Scopes, rule.Scope) && rule.Access == AccessDenied { 72 return ErrForbidden 73 } else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted { 74 return nil 75 } 76 } 77 78 // if no rules matched then return forbidden 79 return ErrForbidden 80 } 81 82 // include is a helper function which checks to see if the slice contains the value. includes is 83 // not case sensitive. 84 func include(slice []string, val string) bool { 85 for _, s := range slice { 86 if strings.EqualFold(s, val) { 87 return true 88 } 89 } 90 return false 91 }