Code refactoring is not an easy job, but it has to be done in most of the times. I just completed the lightweight role based access control library: goRBAC’s refactoring. There are some feedbacks and questions about the design and usage. I think it would be better writing something to share some design ideas and practice principles which will make things easier.
The master branch of current goRBAC’s source code is intended to be Version 2. While the previous version has been tagged as Version 1 with the v1.dev branch on Github. And this article will only discuss Version 2 (the master branch).
The new design separates goRBAC into 3 parts: core, interfaces & implements, and helper functions.
The core is the major part of goRBAC and manages inheritance relationship of roles. It contains only one struct `RBAC` with 8 methods. It’s easy to understand and use. What you need is reading the document.
`Role` is the only interface communicating directly with `RBAC`. It has two methods: `Id` returns the identity of a role and `Permit` checks if the permission is permitted to a role. For example, you can implement a Role named `God` and has all permissions as the God.
type God struct {} func(g *God) Id() string { return "God" } func(g *God) Permit(p Permission) bool { return true }
For making things easier, goRBAC has a built-in role `StdRole` which can be used in a real-life application and also be a good example for anyone who want to write their own `Role` implementation. Moreover, when you implement your own `Role`, struct embedding can be your good friend.
Eg.,
type MyOwnRole struct { gorbac.StdRole // extra fields } // extra functions
Another important interface is `Permission` which is only communicating with `Role`. It also has two methods: `Id` and `Match`. `Id` is the identity of a permission. `Match` is used to detect if current permission matches another one.
The library also supplies two built-in structs for `Permission`.
First one is `StdPermission` which uses `Id` to detect if two permissions are matching.
Another one is `LayerPermission` which supports multi-layers matching and can be very useful to design something like access management of control panels. To be exact, permission “admin” matches “admin:article” and “admin:article:delete”; “admin:article” matches “admin:article:delete”, but you will get a negative result by reversing matching.
And when you have some “hard mode” access control, implementing your own `Permission` is a good idea.
Eg.,
type FuzzPermission struct { IdStr string } func(f *FuzzPermission) Id() string { return f.IdStr } func(f *FuzzPermission) Permit(p Permission) bool { if f.IdStr == p.Id() { // exactly matched return true } q, ok := p.(*FuzzPermission) if !ok { // Not same type return false } return strings.Contains(f.IdStr, p.Id()) // Well, I'm not sure what circumstance can use this kind of permissions }
To be continued …
有疑问加站长微信联系(非本文作者)