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