rbac

package module
v1.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2019 License: MIT Imports: 5 Imported by: 4

README

RBAC - Simple, concurrent Role Based Access Control(GO)

Build Status Go Report Card Codecov GoDoc GitHub release License

RBAC is role based access control library for GOlang. At core uses sync.Map so, it can be used from multiple goroutines concurrently. "Keep it simple" is also in core.

It supports role inheritance.

It can be used in middleware(example for echo framework is given )

Go 1.9+ is required.

It is built on;

Type Description Example
Action Defines what can possible for a permission Create,Read,Update...
Permission Defines permission related to a resource to be accessed users
Role Defines group of permissions with defined actions admin

Usage

Library usage has 2 phases:

  • Development
  • Runtime
Development Phase

First get an instance for RBAC

import "github.com/euroteltr/rbac"

R := rbac.New(nil)
// you can pass a logger to constructor also:
// R := rbac.New(rbac.ConsoleLogger)

During development you will register your permissions for your each resource with valid actions for that permission:

// You can also use rbac.CRUD for those crud actions
usersPerm, err := R.RegisterPermission("users", "User resource", rbac.Create, rbac.Read, rbac.Update, rbac.Delete)
if err != nil {
    panic(err)
}

userPerm is defined with CRUD actions, which means that any action not in that list will be invalid. You can define your own actions( like ApproveAction := rbac.Action("approve")) and add them also.

Runtime Phase

At run time we define our roles and permit permissions to them.

adminRole, err := R.RegisterRole("admin", "Admin role")
if err != nil {
    fmt.Printf("can not add admin role, err: %v\n", err)
}
if err = R.Permit(adminRole.ID, usersPerm, rbac.CRUD, ApproveAction); err != nil {
    fmt.Errorf("can not permit crud and ApproveAction actions to role %s\n", adminRole.ID)
}

Now we can check if a role is granted some permission:


if !R.IsGranted(adminRole.ID, usersPerm, rbac.Write) {
    fmt.Printf("admin role does not have write grant on users\n")
}else{
    fmt.Printf("admin role does have write grant on users\n")
}

// You can also check by perm.ID also
if !R.IsGrantedStr("admin", "users", rbac.CRUD) {
    fmt.Printf("admin role does not have CRUD grants on users\n")
}else{
    fmt.Printf("admin role does have CRUD grants on users\n")
}

Persisting and Loading

rbac.RBAC is json compatible. You can dump all data in RBAC instance to JSON:

b, err := json.Marshal(R)
if err != nil {
    fmt.Printf("rback marshall failed with %v\n", err)
}else{
    fmt.Printf("RBAC: %s", b)
}

Also you can use builtin SaveJSON function to save to a io.Writer:

filename := "/tmp/rbac.json"
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
    fmt.Printf("can not create json file %s, err: %v\n", filename, err)
    return err
}
defer f.Close()
if err = R.SaveJSON(f); err != nil {
    fmt.Printf("unable to save to json file, err:%v\n", err)
}

And load it from file:

filename := "/tmp/rbac.json"
f, err := os.Open(filename)
if err != nil {
    return err
}
defer f.Close()
if err = R.LoadJSON(f); err != nil {
    fmt.Errorf("unable to load from json file, err:%v\n", err)
}

In dumped JSON root permissions part is just for reference. Root roles is the part you can modify in file and reload it to define Roles with Permissions.

{
  "permissions": [
    {
      "id": "users",
      "description": "User resource",
      "actions": [
        "create",
        "read",
        "update",
        "delete"
      ]
    }
  ],
  "roles": [
    {
      "id": "admin",
      "description": "Admin role",
      "grants": {
        "users": [
          "create",
          "read",
          "update",
          "delete"
        ]
      },
      "parents": []
    }
  ]
}

Role inheritance

A Role can have parent Roles. You can add a parent Role like this:

// Add a new role
sysAdmRole, err := R.RegisterRole("sysadm", "System admin role")
if err != nil {
    fmt.Printf("can not add agent role, err: %v\n", err)
}

// Now add adminRole as parent
if err = sysAdmRole.AddParent(adminRole); err != nil {
    fmt.Printf("adding parent role failed with: %v\n", err)
}

// Now all permissions in adminRole will be also valid for sysAdmRole
if R.IsGranted(sysAdmRole.ID, usersPerm, rbac.CRUD) {
    fmt.Printf("sysadmin role has all crud actions granted\n")
}

If circular parent reference is found, you'll get error while running AddParent.

Usage as middleware

You can check example middleware function for echo framework here

Middleware usage with granular permissions

If you want user role may modify his own user resource, but not others, you can build a wrapper for RBAC.IsGranted function like(example for echo framework:

// idParam is the parameter name where user_id for data will be gotten
func isGrantedResource(perm *rbac.Permission, action rbac.Action, idParam string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            rolesI := c.Get("roles")
            if rolesI == nil {
                log.Errorf("No rbac roles key %s", "roles")
            } else if config.RBAC.AnyGranted(rolesI.([]string), perm, actions...) {
                // Get id parameter from route,form... example from: "user/:id"
                userID, err := strconv.ParseInt(c.Param(idParam), 10, 64)
                if err != nil {
                    return http.StatusBadRequest
                }
                // Get current user_id and check if data belongs to current user
                userIDI := c.Get("user_id")
                if userIDI==nil || userIDI.(int64) != userID {
                    return http.StatusForbidden
                }
                return next(c)
            }
            return echo.ErrUnauthorized
        }
    }
}

Documentation

Overview

Package rbac is role based access control library. At core uses `sync.Map` so, it can be used from multiple goroutines concurrently.package rbac

rbac is dependend on these terms:

  • Action Defines what can possible for a permission
  • Permission Defines permission related to a resource to be accessed
  • Role Defines group of permissions with defined actions

Usage:

Library usage has 2 phases:

  • Development
  • Runtime

Development Phase

First get an instance for `RBAC`

import "github.com/euroteltr/rbac"

R := rbac.New(nil)
// you can pass a logger to constructor also:
// R := rbac.New(rbac.ConsoleLogger)

During development you will register your permissions for your each resource with valid actions for that permission:

// You can also use rbac.CRUD for those crud actions
usersPerm, err := R.RegisterPermission("users", "User resource", rbac.Create, rbac.Read, rbac.Update, rbac.Delete)
if err != nil {
	panic(err)
}

`userPerm` is defined with CRUD actions, which means that any action not in that list will be invalid. You can define your own actions( like `ApproveAction := rbac.Action("approve")`) and add them also.

Runtime Phase:

At run time we define our roles and permit permissions to them.

adminRole, err := R.RegisterRole("admin", "Admin role")
if err != nil {
	fmt.Printf("can not add admin role, err: %v\n", err)
}
if err = R.Permit(adminRole.ID, usersPerm, rbac.CRUD, ApproveAction); err != nil {
	fmt.Errorf("can not permit crud and ApproveAction actions to role %s\n", adminRole.ID)
}

Now we can check if a role is granted some permission:

if !R.IsGranted(adminRole.ID, usersPerm, rbac.Write) {
	fmt.Printf("admin role does not have write grant on users\n")
}else{
	fmt.Printf("admin role does have write grant on users\n")
}

// You can also check by perm.ID also
if !R.IsGrantedStr("admin", "users", rbac.CRUD) {
	fmt.Printf("admin role does not have CRUD grants on users\n")
}else{
	fmt.Printf("admin role does have CRUD grants on users\n")
}

Persisting and Loading

`rbac.RBAC` is `json` compatible. You can dump all data in `RBAC` instance to JSON:

b, err := json.Marshal(R)
if err != nil {
	fmt.Printf("rback marshall failed with %v\n", err)
}else{
	fmt.Printf("RBAC: %s", b)
}

Also you can use builtin SaveJSON function to save to a io.Writer:

filename := "/tmp/rbac.json"
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
	fmt.Printf("can not load json file %s, err: %v\n", filename, err)
	return err
}
defer f.Close()
if err = R.SaveJSON(f); err != nil {
	fmt.Printf("unable to save to json file, err:%v\n", err)
}

And load it from file:

filename := "/tmp/rbac.json"
f, err := os.Open(filename)
if err != nil {
	return err
}
defer f.Close()
if err = R.LoadJSON(f); err != nil {
	fmt.Errorf("unable to load from json file, err:%v\n", err)
}

In dumped JSON root "permissions" part is for reference. Root `roles` is the part you can modify in file and reload it to define `Role`s with `Permission`s.

{
"permissions": [
	{
	"id": "users",
	"description": "User resource",
	"actions": [
		"create",
		"read",
		"update",
		"delete"
	]
	}
],
"roles": [
	{
	"id": "admin",
	"description": "Admin role",
	"grants": {
		"users": [
		"create",
		"read",
		"update",
		"delete"
		]
	},
	"parents": []
	}
]
}

Role inheritance

A `Role` can have parent `Role`s. You can add a parent `Role` like this:

// Add a new role
sysAdmRole, err := R.RegisterRole("sysadm", "System admin role")
if err != nil {
	fmt.Printf("can not add agent role, err: %v\n", err)
}

// Now add adminRole as parent
if err = sysAdmRole.AddParent(adminRole); err != nil {
	fmt.Printf("adding parent role failed with: %v\n", err)
}

// Now all permissions in adminRole will be also valid for sysAdmRole
if R.IsGranted(sysAdmRole.ID, usersPerm, rbac.CRUD) {
	fmt.Printf("sysadmin role has all crud actions granted\n")
}

If circular parent reference is found, you'll get error while running `AddParent`.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetLogger

func SetLogger(logger Logger)

SetLogger sets rbac logger

Types

type Action

type Action string

Action is permission action

const (
	// None is for empty action
	None Action = ""
	// Create is for create action
	Create Action = "create"
	// Read is for read action
	Read Action = "read"
	// Update is for  update action
	Update Action = "update"
	// Delete is for delete action
	Delete Action = "delete"
	// CRUD is for, create+read+update+delete permissions
	CRUD Action = "crud"
	// Download is for downloading action
	Download = "download"
	// Upload is for uploading action
	Upload = "upload"
)

type ConsoleLogger

type ConsoleLogger struct {
}

ConsoleLogger is console logger

func (*ConsoleLogger) Debugf

func (nl *ConsoleLogger) Debugf(format string, args ...interface{})

Debugf is for debug logging

func (*ConsoleLogger) Errorf

func (nl *ConsoleLogger) Errorf(format string, args ...interface{})

Errorf is for error logging

type Logger

type Logger interface {
	Debugf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
}

Logger is interface for logger to be used in RBAC

func NewConsoleLogger

func NewConsoleLogger() Logger

NewConsoleLogger returns a new ConsoleLogger

func NewNullLogger

func NewNullLogger() Logger

NewNullLogger returns a new NullLogger

type NullLogger

type NullLogger struct {
}

NullLogger is null logger

func (*NullLogger) Debugf

func (nl *NullLogger) Debugf(format string, args ...interface{})

Debugf is for debug logging

func (*NullLogger) Errorf

func (nl *NullLogger) Errorf(format string, args ...interface{})

Errorf is for error logging

type Permission

type Permission struct {
	ID          string
	Description string
	sync.Map    // key action, val nil
}

Permission defines rbac permission

func (*Permission) Actions

func (p *Permission) Actions() []Action

Actions returns list of Actions

func (*Permission) ActionsStrSlice

func (p *Permission) ActionsStrSlice() []string

ActionsStrSlice returns list of Actions as sorted string slice

func (*Permission) MarshalJSON

func (p *Permission) MarshalJSON() ([]byte, error)

MarshalJSON serializes a Permission to JSON

func (*Permission) String

func (p *Permission) String() string

String returns as string

type RBAC

type RBAC struct {
	sync.Map // key: role.ID, value: role
	// contains filtered or unexported fields
}

RBAC is role bases access control manager

func New

func New(logger Logger) *RBAC

New returns a new RBAC instance

func (*RBAC) AllGrantInherited

func (r *RBAC) AllGrantInherited(roleIDs []string, perm *Permission, action ...Action) bool

AllGrantInherited checks if all roles have the permission.

func (*RBAC) AllGrantInheritedStr

func (r *RBAC) AllGrantInheritedStr(roleIDs []string, permName string, action ...Action) bool

AllGrantInheritedStr checks if all roles have the permission.

func (*RBAC) AllGranted

func (r *RBAC) AllGranted(roleIDs []string, perm *Permission, action ...Action) (res bool)

AllGranted checks if all roles have the permission.

func (*RBAC) AllGrantedStr

func (r *RBAC) AllGrantedStr(roleIDs []string, permName string, action ...Action) (res bool)

AllGrantedStr checks if all roles have the permission.

func (*RBAC) AnyGrantInherited

func (r *RBAC) AnyGrantInherited(roleIDs []string, perm *Permission, action ...Action) (res bool)

AnyGrantInherited checks if any role has the permission.

func (*RBAC) AnyGrantInheritedStr

func (r *RBAC) AnyGrantInheritedStr(roleIDs []string, permName string, action ...Action) (res bool)

AnyGrantInheritedStr checks if any role has the permission.

func (*RBAC) AnyGranted

func (r *RBAC) AnyGranted(roleIDs []string, perm *Permission, action ...Action) (res bool)

AnyGranted checks if any role has the permission.

func (*RBAC) AnyGrantedStr

func (r *RBAC) AnyGrantedStr(roleIDs []string, permName string, action ...Action) (res bool)

AnyGrantedStr checks if any role has the permission.

func (*RBAC) Clone

func (r *RBAC) Clone(roles bool) (trg *RBAC)

Clone clones RBAC instance

func (*RBAC) GetAllPermissions

func (r *RBAC) GetAllPermissions(roleIDs []string) map[string][]Action

GetAllPermissions returns granted permissions for a role(including inherited permissions from parents)

func (*RBAC) GetPermission

func (r *RBAC) GetPermission(permissionID string) *Permission

GetPermission returns the permission if exists. perm is nil if not found.

func (*RBAC) GetRole

func (r *RBAC) GetRole(roleID string) *Role

GetRole finds and returns role from instance, if role is not found returns nil

func (*RBAC) IsGrantInherited

func (r *RBAC) IsGrantInherited(roleID string, perm *Permission, actions ...Action) bool

IsGrantInherited checks if a role with target permission and actions has a grant

func (*RBAC) IsGrantInheritedStr

func (r *RBAC) IsGrantInheritedStr(roleID string, permID string, actions ...Action) bool

IsGrantInheritedStr checks if permID is granted with target actions for role

func (*RBAC) IsGranted

func (r *RBAC) IsGranted(roleID string, perm *Permission, actions ...Action) bool

IsGranted checks if a role with target permission and actions has a grant

func (*RBAC) IsGrantedStr

func (r *RBAC) IsGrantedStr(roleID string, permID string, actions ...Action) bool

IsGrantedStr checks if permID is granted with target actions for role

func (*RBAC) IsPermissionExist

func (r *RBAC) IsPermissionExist(permissionID string, action Action) (res bool)

IsPermissionExist checks if a permission with target ID and action is defined

func (*RBAC) IsRoleExist

func (r *RBAC) IsRoleExist(roleID string) (res bool)

IsRoleExist checks if a role with target ID is defined

func (*RBAC) LoadJSON

func (r *RBAC) LoadJSON(reader io.Reader) error

LoadJSON loads all data from a reader

func (*RBAC) MarshalJSON

func (r *RBAC) MarshalJSON() ([]byte, error)

MarshalJSON serializes a all roles to JSON

func (*RBAC) Permissions

func (r *RBAC) Permissions() []*Permission

Permissions returns all Permissions

func (*RBAC) Permit

func (r *RBAC) Permit(roleID string, perm *Permission, actions ...Action) error

Permit grants a permission with defined actions to a role

func (*RBAC) RegisterPermission

func (r *RBAC) RegisterPermission(permissionID, description string, actions ...Action) (*Permission, error)

RegisterPermission defines and registers a permission

func (*RBAC) RegisterRole

func (r *RBAC) RegisterRole(roleID string, description string) (*Role, error)

RegisterRole defines and registers a role

func (*RBAC) RemoveRole

func (r *RBAC) RemoveRole(roleID string) error

RemoveRole deletes role from instance

func (*RBAC) Revoke

func (r *RBAC) Revoke(roleID string, perm *Permission, actions ...Action) error

Revoke removes a permission from a role

func (*RBAC) RoleGrants

func (r *RBAC) RoleGrants() []*RoleGrants

RoleGrants returns all roles

func (*RBAC) Roles

func (r *RBAC) Roles() (res []*Role)

Roles returns all registered roles

func (*RBAC) SaveJSON

func (r *RBAC) SaveJSON(writer io.Writer) (err error)

SaveJSON saves all to a writer

func (*RBAC) UnmarshalJSON

func (r *RBAC) UnmarshalJSON(b []byte) (err error)

UnmarshalJSON parses RBAC from JSON

type Role

type Role struct {
	ID          string     `json:"id"`
	Description string     `json:"description"`
	sync.Map    `json:"-"` // key: permissionID, values sync.Map[action]=true/false
	// contains filtered or unexported fields
}

Role defines a role

func (*Role) AddParent

func (r *Role) AddParent(parentRole *Role) error

AddParent Adds parent role

func (*Role) HasAncestor

func (r *Role) HasAncestor(parentID string) bool

HasAncestor checks if a role is in parent roles

func (*Role) HasParent

func (r *Role) HasParent(parentID string) bool

HasParent checks if a role is in parent roles

func (*Role) ParentIDs

func (r *Role) ParentIDs() []string

ParentIDs return a list of parent role IDs

func (*Role) Parents

func (r *Role) Parents() []*Role

Parents returns list of parent roles

func (*Role) RemoveParent

func (r *Role) RemoveParent(parentRole *Role) error

RemoveParent removes parent role

type RoleGrants

type RoleGrants struct {
	ID          string    `json:"id"`
	Description string    `json:"description"`
	Grants      grantsMap `json:"grants"`
	Parents     []string  `json:"parents"`
}

RoleGrants is used during JSON Marshalling

Directories

Path Synopsis
middlewares

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL