Skip to content

Commit 4f8b077

Browse files
committed
refactor: add validation logic before removing role or permission
1 parent 859a4a8 commit 4f8b077

3 files changed

Lines changed: 172 additions & 3 deletions

File tree

pkg/errors.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ var (
44
ErrAlreadyExists = errorString("already exists")
55
ErrInvalidResourceOrAction = errorString("invalid resource or action")
66
ErrInvalidName = errorString("invalid name")
7+
ErrNotFound = errorString("not found")
8+
ErrRoleInUse = errorString("role is in use and cannot be removed")
9+
ErrPermissionInUse = errorString("permission is in use and cannot be removed")
10+
ErrDuplicateRole = errorString("role with this name already exists")
11+
ErrDuplicatePermission = errorString("permission with this resource and action already exists")
712
)
813

914
type errorString string

pkg/rbac.go

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ func (r *RBAC) CreateRole(ctx context.Context, name, description string) error {
4343
if n == "" {
4444
return ErrInvalidName
4545
}
46+
47+
exists, err := r.roleNameExists(ctx, n)
48+
if err != nil {
49+
return err
50+
}
51+
if exists {
52+
return ErrDuplicateRole
53+
}
54+
4655
role := Role{ID: uuid.New().String(), Name: n, Description: description}
4756
return r.store.CreateRole(ctx, role)
4857
}
@@ -51,6 +60,19 @@ func (r *RBAC) RemoveRole(ctx context.Context, roleID string) error {
5160
if err := validateUUIDs(roleID); err != nil {
5261
return err
5362
}
63+
64+
if _, err := r.store.GetRole(ctx, roleID); err != nil {
65+
return ErrNotFound
66+
}
67+
68+
inUse, err := r.isRoleInUse(ctx, roleID)
69+
if err != nil {
70+
return err
71+
}
72+
if inUse {
73+
return ErrRoleInUse
74+
}
75+
5476
return r.store.RemoveRole(ctx, roleID)
5577
}
5678

@@ -60,6 +82,15 @@ func (r *RBAC) CreatePermission(ctx context.Context, resource, action string) er
6082
if res == "" || a == "" {
6183
return ErrInvalidResourceOrAction
6284
}
85+
86+
exists, err := r.permissionExists(ctx, res, a)
87+
if err != nil {
88+
return err
89+
}
90+
if exists {
91+
return ErrDuplicatePermission
92+
}
93+
6394
p := Permission{ID: uuid.New().String(), Resource: res, Action: a}
6495
return r.store.CreatePermission(ctx, p)
6596
}
@@ -68,20 +99,64 @@ func (r *RBAC) RemovePermission(ctx context.Context, permID string) error {
6899
if err := validateUUIDs(permID); err != nil {
69100
return err
70101
}
102+
103+
if _, err := r.store.GetPermission(ctx, permID); err != nil {
104+
return ErrNotFound
105+
}
106+
107+
inUse, err := r.isPermissionInUse(ctx, permID)
108+
if err != nil {
109+
return err
110+
}
111+
if inUse {
112+
return ErrPermissionInUse
113+
}
114+
71115
return r.store.RemovePermission(ctx, permID)
72116
}
73117

74118
func (r *RBAC) AssignRole(ctx context.Context, subjectID, roleID string) error {
75119
if err := validateUUIDs(roleID); err != nil {
76120
return err
77121
}
122+
123+
if _, err := r.store.GetRole(ctx, roleID); err != nil {
124+
return ErrNotFound
125+
}
126+
127+
roles, err := r.store.ListSubjectRoles(ctx, subjectID)
128+
if err != nil {
129+
return err
130+
}
131+
for _, role := range roles {
132+
if role.ID == roleID {
133+
return ErrAlreadyExists
134+
}
135+
}
136+
78137
return r.store.AssignRole(ctx, subjectID, roleID)
79138
}
80139

81140
func (r *RBAC) RevokeRole(ctx context.Context, subjectID, roleID string) error {
82141
if err := validateUUIDs(roleID); err != nil {
83142
return err
84143
}
144+
145+
roles, err := r.store.ListSubjectRoles(ctx, subjectID)
146+
if err != nil {
147+
return err
148+
}
149+
found := false
150+
for _, role := range roles {
151+
if role.ID == roleID {
152+
found = true
153+
break
154+
}
155+
}
156+
if !found {
157+
return ErrNotFound
158+
}
159+
85160
return r.store.RevokeRole(ctx, subjectID, roleID)
86161
}
87162

@@ -91,14 +166,14 @@ func (r *RBAC) AddPermissionToRole(ctx context.Context, roleID, permID string) e
91166
}
92167
role, err := r.store.GetRole(ctx, roleID)
93168
if err != nil {
94-
return err
169+
return ErrNotFound
95170
}
96171
if r.roleHasPermission(&role, permID) {
97172
return ErrAlreadyExists
98173
}
99174
p, err := r.store.GetPermission(ctx, permID)
100175
if err != nil {
101-
return err
176+
return ErrNotFound
102177
}
103178
role.Permissions = append(role.Permissions, p)
104179
return r.store.UpdateRole(ctx, role)
@@ -110,8 +185,13 @@ func (r *RBAC) RemovePermissionFromRole(ctx context.Context, roleID, permID stri
110185
}
111186
role, err := r.store.GetRole(ctx, roleID)
112187
if err != nil {
113-
return err
188+
return ErrNotFound
114189
}
190+
191+
if !r.roleHasPermission(&role, permID) {
192+
return ErrNotFound
193+
}
194+
115195
role.Permissions = r.filterOutPermission(role.Permissions, permID)
116196
return r.store.UpdateRole(ctx, role)
117197
}
@@ -132,6 +212,22 @@ func (r *RBAC) RevokeSubjectGrant(ctx context.Context, subjectID, grantID string
132212
if err := validateUUIDs(grantID); err != nil {
133213
return err
134214
}
215+
216+
grants, err := r.store.ListSubjectGrants(ctx, subjectID)
217+
if err != nil {
218+
return err
219+
}
220+
found := false
221+
for _, grant := range grants {
222+
if grant.ID == grantID {
223+
found = true
224+
break
225+
}
226+
}
227+
if !found {
228+
return ErrNotFound
229+
}
230+
135231
return r.store.RevokeSubjectGrant(ctx, subjectID, grantID)
136232
}
137233

@@ -212,3 +308,70 @@ func validateUUIDs(ids ...string) error {
212308
}
213309
return nil
214310
}
311+
312+
313+
func (r *RBAC) roleNameExists(ctx context.Context, name string) (bool, error) {
314+
roles, err := r.store.ListRoles(ctx)
315+
if err != nil {
316+
return false, err
317+
}
318+
for _, role := range roles {
319+
if role.Name == name {
320+
return true, nil
321+
}
322+
}
323+
return false, nil
324+
}
325+
326+
327+
func (r *RBAC) permissionExists(ctx context.Context, resource, action string) (bool, error) {
328+
perms, err := r.store.ListPermissions(ctx)
329+
if err != nil {
330+
return false, err
331+
}
332+
for _, p := range perms {
333+
if p.Resource != resource {
334+
continue
335+
}
336+
if p.Action != action {
337+
continue
338+
}
339+
return true, nil
340+
}
341+
return false, nil
342+
}
343+
344+
345+
func (r *RBAC) isRoleInUse(ctx context.Context, roleID string) (bool, error) {
346+
subjects, err := r.store.ListSubjects(ctx)
347+
if err != nil {
348+
return false, err
349+
}
350+
for _, subjectID := range subjects {
351+
roles, err := r.store.ListSubjectRoles(ctx, subjectID)
352+
if err != nil {
353+
return false, err
354+
}
355+
for _, role := range roles {
356+
if role.ID == roleID {
357+
return true, nil
358+
}
359+
}
360+
}
361+
return false, nil
362+
}
363+
364+
func (r *RBAC) isPermissionInUse(ctx context.Context, permID string) (bool, error) {
365+
roles, err := r.store.ListRoles(ctx)
366+
if err != nil {
367+
return false, err
368+
}
369+
for _, role := range roles {
370+
for _, p := range role.Permissions {
371+
if p.ID == permID {
372+
return true, nil
373+
}
374+
}
375+
}
376+
return false, nil
377+
}

pkg/store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Store interface {
1919
// Subject role bindings
2020
AssignRole(ctx context.Context, subjectID, roleID string) error
2121
RevokeRole(ctx context.Context, subjectID, roleID string) error
22+
ListSubjects(ctx context.Context) ([]string, error)
2223
ListSubjectRoles(ctx context.Context, subjectID string) ([]Role, error)
2324

2425
// Subject direct grants

0 commit comments

Comments
 (0)