3
0
corteza/server/pkg/rbac/resource.go

151 lines
3.0 KiB
Go

package rbac
import (
"path"
"strings"
"github.com/spf13/cast"
)
type (
resource string
Resource interface {
RbacResource() string
}
resourceDicter interface {
Dict() map[string]interface{}
}
)
const (
nsSep = "::"
cmpSep = ":"
pathSep = "/"
wildcard = "*"
)
// NewResource constructs untyped resource from the given string
//
// This is a utility method that should not be used for standard permission checking and granting
// it's intended to be used for testing end permission evaluation where we do not have access to the resource struct
func NewResource(s string) Resource {
return resource(s)
}
// RbacResource returns string of an untyped resource
func (t resource) RbacResource() string {
return string(t)
}
// HasWildcards returns true if the given resource has wildcards
func (t resource) HasWildcards() bool {
return hasWildcards(string(t))
}
// ResourceType extracts 1st part of the resource
//
// ns::cmp:res/c returns ns::cmp:res
// ns::cmp:res/ returns ns::cmp:res
// ns::cmp:res returns ns::cmp:res
func ResourceType(r string) string {
if p := strings.Index(r, pathSep); p > 0 {
return r[:p]
} else {
return r
}
}
func ResourceComponent(r string) string {
var (
t = ResourceType(r)
ns = strings.Index(t, nsSep)
c = strings.LastIndex(t, cmpSep)
)
// make sure that we have both namespace + component separators
if c > ns+1 && ns > -1 {
return t[:c]
} else {
return t
}
}
func ParseResourceID(r string) (string, []uint64) {
const sep = "/"
var (
pp = strings.Split(r, sep)
ids = make([]uint64, 0)
)
for i := 1; i < len(pp); i++ {
ids = append(ids, cast.ToUint64(pp[i]))
}
return pp[0], ids
}
// match returns true if the given resource matches the given pattern
func matchResource(matcher, resource string) (m bool) {
if matcher == resource {
return true
}
m, _ = path.Match(matcher, resource)
return
}
func hasWildcards(resource string) bool {
return strings.Contains(resource, wildcard)
}
// returns level for the given resource match
// In a nutshell, level indicates number of wildcard characters
//
// More defined resources use less wildcards and are on a lower level
func level(r string) (score int) {
var nl bool
for l := len(r) - 1; l > strings.Index(r, pathSep); l-- {
switch r[l] {
case wildcard[0]:
// nop
case pathSep[0]:
// found next resource reference level
score *= 10
nl = false
default:
if !nl {
score += 1
nl = true
}
}
}
return
}
// isSpecific will return true if rule relates to a specific resource
//
// ns::cmp:res/xx/* returns true
// ns::cmp:res/xx/xx returns true
// ns::cmp:res/ returns false
// ns::cmp:res/*/* returns false
func isSpecific(r string) (out bool) {
out = false
parts := strings.Split(r, pathSep)
// remove resource name part
parts = parts[1:]
// check parts for wildcard if it's not empty otherwise return false
for _, p := range parts {
if len(p) == 0 {
continue
}
if p != wildcard && !out {
out = true
}
}
return
}