Introduction
goRBAC provides a lightweight role-based access control (RBAC) implementation in Golang. Normally, the privilege information (roles, parents, and permissions) are saved in the persistent storage, e.g. Database, Files, or Cloud Storage. This post will briefly discuss the technical details of how to load the goRBAC instance from persistent storage and how to save the instance back. In order to make things simple, I will use the JSON file as the persistent storage.
Preparation / Definition
Suppose we have 3 roles: Editor, Photographer and Chief Editor. Each role has the following parents and permissions:
Role | Parents | Permissions |
---|---|---|
editor | NULL | add-text, edit-text, insert-photo |
photographer | NULL | add-photo, edit-photo |
chief-editor | editor, photographer | del-text, del-photo |
Converting the table into two JSON format. One saves the data of roles (`roles.json`), while another stores inheritance information (`inher.json`):
{"editor":["add-text","edit-text","insert-photo"],"photographer":["add-photo","edit-photo"],"chief-editor":["del-text","del-photo"]}
{"chief-editor":["editor","photographer"]}
Because it’s obvious enough, I won’t put any energy to a further explanation. However, if you got any question about this conversion, comments here are welcome.
Loading from JSON file
Loading the information and building goRBAC instance is simple.
First of all, open the JSON file and load all data into a map instance.
// map[RoleId]PermissionIds var jsonRoles map[string][]string // map[RoleId]ParentIds var jsonInher map[string][]string // Load roles information if err := LoadJson("roles.json", &jsonRoles); err != nil { log.Fatal(err) } // Load inheritance information if err := LoadJson("inher.json", &jsonInher); err != nil { log.Fatal(err) }
Then we can start to build the goRBAC instance. There are 3 steps to build a goRBAC instance.
First of all, initialise the instance:
rbac := gorbac.New() permissions := make(gorbac.Permissions)
And we can make `Role(s)` and add them into the goRBAC instance.
// Build roles and add them to goRBAC instance for rid, pids := range jsonRoles { role := gorbac.NewStdRole(rid) for _, pid := range pids { _, ok := permissions[pid] if !ok { permissions[pid] = gorbac.NewStdPermission(pid) } role.Assign(permissions[pid]) } rbac.Add(role) }
Following that, assign the inheritance relationship between roles.
// Assign the inheritance relationship for rid, parents := range jsonInher { if err := rbac.SetParents(rid, parents); err != nil { log.Fatal(err) } }
Common use cases
After loading all the information and get the goRBAC instance on hand, we can start checking the privileges.
You can find the details here.
Saving back to JSON file
If we add some new roles and permissions or modify the relationship between the roles, then writing the changes back to the persistence storage is significant.
The easiest way is using the helper function `Walk`
// Persist the change // map[RoleId]PermissionIds jsonOutputRoles := make(map[string][]string) // map[RoleId]ParentIds jsonOutputInher := make(map[string][]string) SaveJsonHandler := func(r gorbac.Role, parents []string) error { // WARNING: Don't use gorbac.RBAC instance in the handler, // otherwise it causes deadlock. permissions := make([]string, 0) for _, p := range r.(*gorbac.StdRole).Permissions() { permissions = append(permissions, p.ID()) } jsonOutputRoles[r.ID()] = permissions jsonOutputInher[r.ID()] = parents return nil } if err := gorbac.Walk(rbac, SaveJsonHandler); err != nil { log.Fatalln(err) } // Save roles information if err := SaveJson("new-roles.json", &jsonOutputRoles); err != nil { log.Fatal(err) } // Save inheritance information if err := SaveJson("new-inher.json", &jsonOutputInher); err != nil { log.Fatal(err) }
One thing I have to mention is, despite it is a closure, DO NOT use the goRBAC instance in the handler. Otherwise, it will cause deadlock. The basic concept of the `WalkHandler` is grabbing the information from the goRBAC instance which is an READONLY process to the instance. Without any modify, no goRBAC instance access.
Conclution
goRBAC is following the basic idea of Go — Less is exponentially more and it’s inspired by “The Clean Architecture“. Thus the complex functions such as RDBMS interface, NoSQL interface, or authenticating won’t appear in the core. While `Helper` is an important part to make goRBAC easy to use.
We will be alive without help, but it won’t be easy. ;P
Learning it little by little. Extending it step by step.
Leave a Reply