Skip to content

Commit 1e94a37

Browse files
committed
feat: itialimplementation for rbac service with store interface
1 parent 5731b11 commit 1e94a37

6 files changed

Lines changed: 229 additions & 0 deletions

File tree

.github/workflows/go.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Go
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v5
15+
- name: Set up Go
16+
uses: actions/setup-go@v6
17+
with:
18+
go-version: 1.25
19+
- name: Run golangci-lint
20+
uses: golangci/golangci-lint-action@v8
21+
with:
22+
args: run
23+
- name: Format
24+
uses: Jerome1337/gofmt-action@v1.0.5
25+
26+
- name: Run tests
27+
run: go test -v ./...

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module rbac
2+
3+
go 1.24.6

pkg/errors.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package rbac
2+
3+
var (
4+
ErrNotFound = errorString("not found")
5+
ErrAlreadyExists = errorString("already exists")
6+
)
7+
8+
type errorString string
9+
10+
func (e errorString) Error() string { return string(e) }

pkg/rbac.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package rbac
2+
3+
import (
4+
"context"
5+
"strings"
6+
)
7+
8+
type RBAC struct {
9+
store Store
10+
}
11+
12+
func New(s Store) *RBAC {
13+
return &RBAC{store: s}
14+
}
15+
16+
func (r *RBAC) CreateRole(ctx context.Context, role Role) error {
17+
role.Name = strings.ToLower(role.Name)
18+
return r.store.CreateRole(ctx, role)
19+
}
20+
21+
func (r *RBAC) RemoveRole(ctx context.Context, roleID string) error {
22+
return r.store.RemoveRole(ctx, roleID)
23+
}
24+
25+
func (r *RBAC) CreatePermission(ctx context.Context, p Permission) error {
26+
p.Resource = strings.ToLower(p.Resource)
27+
p.Action = strings.ToLower(p.Action)
28+
return r.store.CreatePermission(ctx, p)
29+
}
30+
31+
func (r *RBAC) RemovePermission(ctx context.Context, id string) error {
32+
return r.store.RemovePermission(ctx, id)
33+
}
34+
35+
func (r *RBAC) AssignRole(ctx context.Context, subjectID, roleID string) error {
36+
return r.store.AssignRole(ctx, subjectID, roleID)
37+
}
38+
39+
func (r *RBAC) RevokeRole(ctx context.Context, subjectID, roleID string) error {
40+
return r.store.RevokeRole(ctx, subjectID, roleID)
41+
}
42+
43+
func (r *RBAC) AddPermissionToRole(ctx context.Context, roleID string, permID string) error {
44+
role, err := r.store.GetRole(ctx, roleID)
45+
if err != nil {
46+
return err
47+
}
48+
for _, p := range role.Permissions {
49+
if p.ID == permID {
50+
return ErrAlreadyExists
51+
}
52+
}
53+
p, err := r.store.GetPermission(ctx, permID)
54+
if err != nil {
55+
return err
56+
}
57+
role.Permissions = append(role.Permissions, p)
58+
return r.store.UpdateRole(ctx, role)
59+
}
60+
61+
func (r *RBAC) RemovePermissionFromRole(ctx context.Context, roleID string, permID string) error {
62+
role, err := r.store.GetRole(ctx, roleID)
63+
if err != nil {
64+
return err
65+
}
66+
filtered := role.Permissions[:0]
67+
for _, p := range role.Permissions {
68+
if p.ID != permID {
69+
filtered = append(filtered, p)
70+
}
71+
72+
}
73+
role.Permissions = filtered
74+
return r.store.UpdateRole(ctx, role)
75+
}
76+
77+
// Direct grants
78+
func (r *RBAC) GrantSubject(ctx context.Context, subjectID string, grant Grant) error {
79+
80+
grant.Resource = strings.ToLower(grant.Resource)
81+
grant.Action = strings.ToLower(grant.Action)
82+
return r.store.GrantSubject(ctx, subjectID, grant)
83+
}
84+
85+
func (r *RBAC) RevokeSubjectGrant(ctx context.Context, subjectID, grantID string) error {
86+
return r.store.RevokeSubjectGrant(ctx, subjectID, grantID)
87+
}
88+
89+
func (r *RBAC) Can(ctx context.Context, subjectID, action, resource string, resourceID ...string) (bool, error) {
90+
grants, err := r.store.ListSubjectGrants(ctx, subjectID)
91+
if err != nil {
92+
return false, err
93+
}
94+
roles, err := r.store.ListSubjectRoles(ctx, subjectID)
95+
if err != nil {
96+
return false, err
97+
}
98+
99+
id := ""
100+
if len(resourceID) > 0 {
101+
id = resourceID[0]
102+
}
103+
res := strings.ToLower(resource)
104+
act := strings.ToLower(action)
105+
106+
//direct grants
107+
for _, g := range grants {
108+
if strings.ToLower(g.Resource) != res {
109+
continue
110+
}
111+
if strings.ToLower(g.Action) != act {
112+
continue
113+
}
114+
if g.ResourceID != id && g.ResourceID != "*" {
115+
continue
116+
}
117+
return true, nil
118+
}
119+
120+
//role permissions
121+
for _, role := range roles {
122+
for _, p := range role.Permissions {
123+
if strings.ToLower(p.Resource) != res {
124+
continue
125+
}
126+
if strings.ToLower(p.Action) != act {
127+
continue
128+
}
129+
return true, nil
130+
}
131+
}
132+
133+
return false, nil
134+
}

pkg/store.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package rbac
2+
3+
import "context"
4+
5+
type Store interface {
6+
// Roles
7+
CreateRole(ctx context.Context, role Role) error
8+
GetRole(ctx context.Context, roleID string) (Role, error)
9+
UpdateRole(ctx context.Context, role Role) error
10+
RemoveRole(ctx context.Context, roleID string) error
11+
ListRoles(ctx context.Context) ([]Role, error)
12+
13+
// Permissions
14+
CreatePermission(ctx context.Context, p Permission) error
15+
GetPermission(ctx context.Context, id string) (Permission, error)
16+
ListPermissions(ctx context.Context) ([]Permission, error)
17+
RemovePermission(ctx context.Context, id string) error
18+
19+
// Subject role bindings
20+
AssignRole(ctx context.Context, subjectID, roleID string) error
21+
RevokeRole(ctx context.Context, subjectID, roleID string) error
22+
ListSubjectRoles(ctx context.Context, subjectID string) ([]Role, error)
23+
24+
// Subject direct grants
25+
GrantSubject(ctx context.Context, subjectID string, g Grant) error
26+
RevokeSubjectGrant(ctx context.Context, subjectID string, grantID string) error
27+
ListSubjectGrants(ctx context.Context, subjectID string) ([]Grant, error)
28+
}

pkg/types.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package rbac
2+
3+
type Permission struct {
4+
ID string `json:"id"`
5+
Resource string `json:"resource"`
6+
Action string `json:"action"`
7+
}
8+
9+
type Role struct {
10+
ID string `json:"id"`
11+
Name string `json:"name"`
12+
Description string `json:"description"`
13+
Permissions []Permission `json:"permissions"`
14+
}
15+
16+
type Grant struct {
17+
ID string `json:"id"`
18+
Resource string `json:"resource"`
19+
ResourceID string `json:"resource_id"`
20+
Action string `json:"action"`
21+
}
22+
23+
type User struct {
24+
ID string `json:"id"`
25+
Roles []Role `json:"roles"`
26+
Grants []Grant `json:"grants"`
27+
}

0 commit comments

Comments
 (0)