guard
API
guard
packageAPI reference for the guard
package.
Imports
(6)
S
struct
CompiledPolicy
CompiledPolicy holds pre-compiled authorization rules for a resource type.
core/guard/policy.go:18-23
type CompiledPolicy struct
Methods
Evaluate
Method
Parameters
Returns
error
func (*CompiledPolicy) Evaluate(user Identity, resourceVal reflect.Value, action string) error
{
allowed := false
ruleFound := false
checkStatic := func(act string) {
if allowedRoles, ok := p.StaticRules[act]; ok {
ruleFound = true
userRoles := user.GetRoles()
for _, ur := range userRoles {
if allowedRoles[ur] {
allowed = true
return
}
if allowedRoles["*"] {
allowed = true
return
}
}
}
}
checkStatic(action)
if allowed {
return nil
}
checkStatic("*")
if allowed {
return nil
}
ruleIndices := p.actionIndex[action]
rRuleIndices := make([]int, len(ruleIndices))
copy(rRuleIndices, ruleIndices)
rRuleIndices = append(rRuleIndices, p.wildcardIndex...)
for _, idx := range rRuleIndices {
if idx >= len(p.DynamicRules) {
continue
}
rule := p.DynamicRules[idx]
ruleFound = true
fieldVal := resourceVal.Field(rule.FieldIndex)
dynamicRoles := extractRoles(fieldVal, user.GetID())
userRoles := user.GetRoles()
for _, dr := range dynamicRoles {
for _, ur := range userRoles {
if dr == ur {
allowed = true
break
}
}
if allowed {
break
}
}
if allowed {
break
}
}
if !ruleFound {
return fmt.Errorf("no policy defined for action '%s'", action)
}
if !allowed {
return fmt.Errorf("permission denied for action '%s'", action)
}
return nil
}
Fields
| Name | Type | Description |
|---|---|---|
| StaticRules | map[string]map[string]bool | |
| DynamicRules | []DynamicRule | |
| actionIndex | map[string][]int | |
| wildcardIndex | []int |
S
struct
DynamicRule
DynamicRule maps a dynamic role field to the actions it governs.
core/guard/policy.go:26-31
type DynamicRule struct
Fields
| Name | Type | Description |
|---|---|---|
| FieldIndex | int | |
| Actions | []string | |
| FieldType | reflect.Type | |
| FieldKind | reflect.Kind |
F
function
getPolicy
Parameters
typ
Returns
core/guard/policy.go:33-40
func getPolicy(typ reflect.Type) *CompiledPolicy
{
if val, ok := policyCache.Load(typ); ok {
return val.(*CompiledPolicy)
}
policy := compilePolicy(typ)
policyCache.Store(typ, policy)
return policy
}
F
function
compilePolicy
Parameters
typ
Returns
core/guard/policy.go:42-100
func compilePolicy(typ reflect.Type) *CompiledPolicy
{
fields := globalParser.ParseType(typ)
policy := &CompiledPolicy{
StaticRules: make(map[string]map[string]bool),
DynamicRules: make([]DynamicRule, 0),
actionIndex: make(map[string][]int),
wildcardIndex: make([]int, 0),
}
for _, meta := range fields {
permissions := meta.GetAll("can")
roles := meta.GetAll("role")
isDynamicRole := false
staticRoles := make([]string, 0, len(roles))
for _, r := range roles {
if r == "*" {
isDynamicRole = true
} else {
staticRoles = append(staticRoles, r)
}
}
if len(permissions) > 0 {
for _, action := range permissions {
if len(staticRoles) > 0 {
if policy.StaticRules[action] == nil {
policy.StaticRules[action] = make(map[string]bool)
}
for _, r := range staticRoles {
policy.StaticRules[action][r] = true
}
}
}
if isDynamicRole {
dr := DynamicRule{
FieldIndex: meta.Index,
Actions: permissions,
FieldType: meta.Type,
FieldKind: meta.Type.Kind(),
}
idx := len(policy.DynamicRules)
policy.DynamicRules = append(policy.DynamicRules, dr)
for _, action := range permissions {
if action == "*" {
policy.wildcardIndex = append(policy.wildcardIndex, idx)
} else {
policy.actionIndex[action] = append(policy.actionIndex[action], idx)
}
}
}
}
}
return policy
}
F
function
extractRoles
Parameters
val
userID
string
Returns
[]string
core/guard/policy.go:173-202
func extractRoles(val reflect.Value, userID string) []string
{
var roles []string
if val.Kind() == reflect.Map {
for _, key := range val.MapKeys() {
if checker.IsMatch(key, userID) {
roleVal := val.MapIndex(key)
if roleVal.Kind() == reflect.String {
roles = append(roles, roleVal.String())
} else if roleVal.Kind() == reflect.Slice || roleVal.Kind() == reflect.Array {
for i := 0; i < roleVal.Len(); i++ {
rv := roleVal.Index(i)
if rv.Kind() == reflect.String {
roles = append(roles, rv.String())
}
}
}
}
}
} else if val.Kind() == reflect.String {
roles = append(roles, val.String())
} else if val.Kind() == reflect.Slice {
for i := 0; i < val.Len(); i++ {
rv := val.Index(i)
if rv.Kind() == reflect.String {
roles = append(roles, rv.String())
}
}
}
return roles
}
S
struct
Guard
Guard provides the authorization engine.
core/guard/guard.go:15-15
type Guard struct
Methods
GetRoles
Method
GetRoles returns all roles resolved for the identity on the resource.
Parameters
user
Identity
resource
any
Returns
[]string
error
func (*Guard) GetRoles(user Identity, resource any) ([]string, error)
{
if user == nil {
return nil, errors.New("identity is nil")
}
if resource == nil {
return nil, errors.New("resource is nil")
}
val := reflect.ValueOf(resource)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return nil, errors.New("resource must be a struct or pointer to struct")
}
policy := getPolicy(val.Type())
matchedRoles := make(map[string]bool)
userRoles := user.GetRoles()
userRolesMap := make(map[string]bool, len(userRoles))
for _, r := range userRoles {
userRolesMap[r] = true
}
for _, roleMap := range policy.StaticRules {
for r := range roleMap {
if r == "*" {
continue
}
if userRolesMap[r] {
matchedRoles[r] = true
}
}
}
for _, rule := range policy.DynamicRules {
fieldVal := val.Field(rule.FieldIndex)
roles := extractRoles(fieldVal, user.GetID())
for _, r := range roles {
matchedRoles[r] = true
}
}
roles := make([]string, 0, len(matchedRoles))
for r := range matchedRoles {
roles = append(roles, r)
}
return roles, nil
}
Can
Method
Can checks if the identity is allowed to perform the action on the resource.
Parameters
Returns
error
func (*Guard) Can(user Identity, resource any, action string) error
{
if user == nil {
return errors.New("identity is nil")
}
if resource == nil {
return errors.New("resource is nil")
}
val := reflect.ValueOf(resource)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return errors.New("resource must be a struct or pointer to struct")
}
policy := getPolicy(val.Type())
return policy.Evaluate(user, val, action)
}
F
function
NewGuard
NewGuard creates a new guard engine.
Returns
core/guard/guard.go:18-20
func NewGuard() *Guard
{
return &Guard{}
}
F
function
Can
Can checks if the identity is allowed to perform the action on the resource.
Parameters
Returns
error
core/guard/guard.go:97-99
func Can(user Identity, resource any, action string) error
{
return NewGuard().Can(user, resource, action)
}