// Matches a role against a compiled globset, and if there is no match, implements legacy behaviour func matchRoleAgainstCompiledGlobset(r string, patterns []string, globs glob.GlobSet) (bool, error) { log.Tracef("[Auth matcher] Testing if role \"%s\" in globset %s", r, globs.String()) match := globs.MatchString(r) if !match && strings.HasSuffix(r, ".*") { // wow many cruft, so horror // // Okay. About this. Old role definitions (which can and should be phased out) allowed pseudo-glob matches in // the role definition itself. So a role authoriser might allow "H4BADMIN.*" to allow admins of *any* H4B // organisation to access an endpoint. Here, to support that behaviour, we compile the role being matched to a // glob, and match it against the globs in the user's globset. Yes, you heard me: we match globs against globs. // Meta. // These old-style globs were only permitted as the last component of a path, and were optional. As .** allows // any number of components, that isn't appropriate. So we test for an exact match without the .* suffix, and a // .* match to match a single component. r = strings.TrimSuffix(r, ".*") r = glob.EscapeGlobComponent(r, RoleGlobOptions) roleGlob, err := glob.Compile(fmt.Sprintf("%s.*", r), RoleGlobOptions) if err != nil { return false, fmt.Errorf("Cannot compile legacy wildcard %s to glob: %v", r, err) } // Recompile the globs; we now don't want the .** suffix globs, err = glob.CompileGlobSet(patterns, RoleGlobOptions) if err != nil { return false, fmt.Errorf("Cannot compile user roles %s to glob set for legacy matching: %s", globs.String(), err.Error()) } // Iterate in reverse, so we can bail early if we hit a match g := globs.Globs() for i := len(g) - 1; i >= 0; i-- { glob := g[i] if glob.String() == r || roleGlob.MatchString(glob.String()) { match = true break } } } return match, nil }
// Compiles a list of roles to a globset func compileRolesToGlobset(patterns []string) (glob.GlobSet, error) { // Append .** to the patterns (old patterns were all prefix matches) p := make([]string, len(patterns)) for i, pattern := range patterns { p[i] = fmt.Sprintf("%s.**", pattern) } // Check if globSet exists in cache cacheKey := strings.Join(p, "|") if globSet, ok := compiledGlobSetCache.Get(cacheKey); ok { return globSet.(glob.GlobSet), nil } globSet, err := glob.CompileGlobSet(p, RoleGlobOptions) if err != nil { return nil, err } // Add new globSet to cache compiledGlobSetCache.Add(cacheKey, globSet) return globSet, nil }