A flexible, storage-agnostic Role-Based Access Control (RBAC) library for Go. Defines core domain types, repository interfaces, and a high-level Manager to handle permission and role checks. Includes a MongoDB implementation, in-memory testing stub, wildcard and fuzzy matching, benchmarks, and unit tests.
-
Storage-agnostic: Define
PermissionRepo,RoleRepo,UserRepo,RolePermissionRepo, andUserRoleRepointerfaces to plug in any backend (MongoDB, SQL, in-memory, etc.). -
High-level Manager:
Managerstruct orchestrates CRUD and business logic: creating/deleting users, roles, permissions; assigning roles and permissions; checking access viaCan. -
Wildcard support:
- Action wildcard (
*) grants all actions on a resource (e.g.survey,*). - Resource single-segment wildcard (
*) matches exactly one segment between dots (e.g.survey.*.testmatchessurvey.foo.test). - Resource multi-segment wildcard (
**) matches zero or more segments (e.g.survey.**.testmatchessurvey.test,survey.foo.test, orsurvey.foo.bar.test). - Global wildcard (
*) on resource matches any resource name (e.g.*).
- Action wildcard (
go get github.com/DarlingGoose/rbacpackage main
import (
"context"
"fmt"
"log/slog"
"github.com/DarlingGoose/rbac"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// MongoDB setup
client, _ := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
db := client.Database("mydb")
// Create store and manager
mgr,err := rbac.NewMongoStoreManager(context.Background(),db)
if err != nil {
slog.Error("failed setting up manager","err",err)
return
}
ctx := context.Background()
// Create a permission and a role
perm := &rbac.Permission{Resource: "survey.*", Action: rbac.ActionAll}
err = mgr.CreatePermission(ctx, perm)
if err != nil {
slog.Error("failed creating permission","err",err)
return
}
role := &rbac.Role{Name: "admin"}
err = mgr.CreateRole(ctx, role)
if err != nil {
slog.Error("failed creating permission","err",err)
return
}
err = mgr.AssignPermissionToRole(ctx, role.ID, perm.ID)
if err != nil {
slog.Error("failed assigning permission to role","err",err)
return
}
// Assign role to user
user := &rbac.User{Username: "alice", Email: "alice@example.com"}
err = mgr.CreateUser(ctx, user)
if err != nil {
slog.Error("failed creating user","err",err)
return
}
err = mgr.AssignRoleToUser(ctx, user.ID, role.ID)
if err != nil {
slog.Error("failed assigning user to role","err",err)
return
}
// Check access
ok, _ := mgr.Can(ctx, user.ID, "survey.123.test", rbac.ActionRead)
fmt.Println("Access granted?", ok)
}-
Unit tests:
go test ./... # includes CRUD, wildcard, fuzzy tests
-
Benchmarks:
go test -bench=. ./...goos: linux goarch: amd64 pkg: github.com/DarlingGoose/rbac cpu: AMD Ryzen 7 3700X 8-Core Processor BenchmarkCan_NoRoles-16 2158071 596.0 ns/op BenchmarkCan_SingleRoleSinglePerm-16 953500 1084 ns/op BenchmarkCan_ManyRolesManyPerms-16 169365 7284 ns/op BenchmarkCan_ResourceWildcard-16 894992 1210 ns/op PASS ok github.com/DarlingGoose/rbac 5.322s
- Fork the repo
- Create a feature branch (
git checkout -b feat/your-feature) - Write code and tests
- Update this README
- Submit a pull request
MIT © Seann Moser