Skip to content

Instantly share code, notes, and snippets.

@kosmikko
Created July 1, 2015 07:57
Show Gist options
  • Save kosmikko/1a1fa2258ea00db2051c to your computer and use it in GitHub Desktop.
Save kosmikko/1a1fa2258ea00db2051c to your computer and use it in GitHub Desktop.
inline struct rbac
package main
import (
"log"
"reflect"
"strings"
)
type RoleAccess struct {
RoleName string
AllowedActions []string
}
func (a *RoleAccess) HasAccess(action string) (isGranted bool) {
for _, a := range a.AllowedActions {
if action == a || a == "*" {
return true
}
}
return false
}
type FieldRBAC struct {
RolesAccess map[string]RoleAccess
}
// parse per field access config
func NewFieldRBAC(rolesConfig []string) (rbac *FieldRBAC) {
rbac = &FieldRBAC{RolesAccess: make(map[string]RoleAccess)}
for _, role := range rolesConfig {
roleACL := strings.Split(role, ":")
roleName, allowedActions := strings.Trim(roleACL[0], " "), strings.Fields(roleACL[1])
acl := RoleAccess{RoleName: roleName, AllowedActions: allowedActions}
rbac.RolesAccess[roleName] = acl
}
return
}
func (r *FieldRBAC) HasAccess(roles []string, action string) (isGranted bool) {
for _, role := range roles {
roleAccess, roleDefined := r.RolesAccess[role]
if roleDefined && roleAccess.HasAccess(action) {
return true
}
}
// check '*' role
roleAccess, roleDefined := r.RolesAccess["*"]
if roleDefined && roleAccess.HasAccess(action) {
return true
}
return false
}
type GlobalAccess struct {
RoleActions map[string]RoleAccess
}
type ACL struct {
// global access control for the struct
global *FieldRBAC
// per field access control
fields map[string]*FieldRBAC
}
// check global access
func (acl *ACL) HasAccess(roles []string, action string) (isGranted bool) {
return acl.global.HasAccess(roles, action)
}
// check global & per field access
func (acl *ACL) HasAccessToFields(roles []string, action string, fields []string) (isGranted bool) {
globalAccess := acl.HasAccess(roles, action)
if !globalAccess {
return false
}
for _, field := range fields {
fieldACL, fieldACLDefined := acl.fields[field]
if fieldACLDefined {
hasAccess := fieldACL.HasAccess(roles, action)
if !hasAccess {
return false
}
}
}
return true
}
func GetFirstTagValue(tag reflect.StructTag, attr string) (value string) {
tagValue := tag.Get(attr)
if len(tagValue) == 0 {
return
}
value = strings.Split(tagValue, ",")[0]
return
}
func GetFieldName(field reflect.StructField) (value string) {
value = field.Tag.Get("access_field_name")
if len(value) > 0 {
return
}
value = GetFirstTagValue(field.Tag, "json")
if len(value) > 0 {
return
}
value = field.Name
return
}
func NewACL(s interface{}) (acl *ACL) {
acl = &ACL{fields: make(map[string]*FieldRBAC)}
tagType := reflect.TypeOf(s)
for i := 0; i < tagType.NumField(); i++ {
field := tagType.Field(i)
access := field.Tag.Get("access")
rolesConfig := strings.Split(access, ",")
fieldName := GetFieldName(field)
if fieldName == "acl" {
acl.global = NewFieldRBAC(rolesConfig)
} else {
acl.fields[fieldName] = NewFieldRBAC(rolesConfig)
}
}
return
}
type User struct {
DisplayName string `access:"admin:*, owner: read update, *: read" json:"display_name"`
Password string `access:"admin:*, owner: read update" json:"password"`
ACL *ACL `access:"admin:*, owner: update, *: read" access_field_name:"acl"`
}
func NewUser() (user User) {
user = User{DisplayName: "Foo Bar"}
user.ACL = NewACL(user)
return
}
func main() {
u := NewUser()
log.Println("done", u.ACL.HasAccess([]string{"admin"}, "delete"))
log.Println(u.ACL.HasAccess([]string{"anon"}, "delete"))
log.Println(u.ACL.HasAccessToFields([]string{"owner"}, "read", []string{"display_name"}))
log.Println(u.ACL.HasAccessToFields([]string{"anon"}, "read", []string{"display_name"}))
log.Println(u.ACL.HasAccessToFields([]string{"anon"}, "read", []string{"display_name", "password"}))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment