Skip to content

Commit

Permalink
feat: add get user permissions method to enforcer
Browse files Browse the repository at this point in the history
Signed-off-by: Khor Shu Heng <khor.heng@gojek.com>
  • Loading branch information
khorshuheng committed Jul 26, 2023
1 parent 5f73563 commit 776ba32
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 0 deletions.
34 changes: 34 additions & 0 deletions api/pkg/authz/enforcer/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type Enforcer interface {
GetUserRoles(ctx context.Context, user string) ([]string, error)
// GetRolePermissions get all permissions directly associated with a role
GetRolePermissions(ctx context.Context, role string) ([]string, error)
// GetUserPermissions get all permissions associated with a user
GetUserPermissions(ctx context.Context, user string) ([]string, error)
// GetRoleMembers get all members for a role
GetRoleMembers(ctx context.Context, role string) ([]string, error)
// UpdateAuthorization update authorization rules in batches
Expand Down Expand Up @@ -131,6 +133,38 @@ func (e *enforcer) GetRolePermissions(ctx context.Context, role string) ([]strin
return permissions, nil
}

func (e *enforcer) GetUserPermissions(ctx context.Context, user string) ([]string, error) {
roles, err := e.GetUserRoles(ctx, user)
if err != nil {
return nil, err
}
permissionSet := sync.Map{}
getPermissionsWorkersGroup := new(errgroup.Group)
for _, role := range roles {
role := role
getPermissionsWorkersGroup.Go(func() error {
permissions, err := e.GetRolePermissions(ctx, role)
for _, permission := range permissions {
permissionSet.Store(permission, true)
}
if err != nil {
return err
}
return nil
})
}
err = getPermissionsWorkersGroup.Wait()
if err != nil {
return nil, err
}
permissions := make([]string, 0)
permissionSet.Range(func(key, value interface{}) bool {
permissions = append(permissions, key.(string))
return true
})
return permissions, nil
}

func (e *enforcer) GetRoleMembers(ctx context.Context, role string) ([]string, error) {
// The permission tree includes the role as the parent node, and the members as the children nodes.
// Hence, we need at least the depth of 2 to get the members. We don't go beyond 2 as we currently
Expand Down
43 changes: 43 additions & 0 deletions api/pkg/authz/enforcer/enforcer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,49 @@ func TestEnforcer_GetRolePermissions(t *testing.T) {
}
}

func TestEnforcer_GetUserPermissions(t *testing.T) {
ketoEnforcer, err := NewEnforcerBuilder().Build()
require.NoError(t, err)
readClient := newKetoClient(ketoRemoteRead)
writeClient := newKetoClient(ketoRemoteWrite)
clearRelations(readClient, writeClient)
updateRequest := NewAuthorizationUpdateRequest()
updateRequest.SetRolePermissions("pages.1.reader", []string{"pages.1.get"})
updateRequest.SetRolePermissions("pages.1.admin", []string{"pages.1.get", "pages.1.post"})
updateRequest.SetRoleMembers("pages.1.reader", []string{"user-1@example.com"})
updateRequest.SetRoleMembers("pages.1.admin", []string{"user-1@example.com"})
err = ketoEnforcer.UpdateAuthorization(context.Background(), updateRequest)
require.NoError(t, err)
tests := []struct {
name string
user string
expectedPermissions []string
}{
{
"user-1 has reader and admin permissions for page 1",
"user-1@example.com",
[]string{
"pages.1.get",
"pages.1.post",
},
},
{
"unknown user has no no permissions",
"anonymous@example.com",
[]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := ketoEnforcer.GetUserPermissions(context.Background(), tt.user)
require.NoError(t, err)
sort.Strings(tt.expectedPermissions)
sort.Strings(res)
require.Equal(t, tt.expectedPermissions, res)
})
}
}

func TestEnforcer_GetRoleMembers(t *testing.T) {
ketoEnforcer, err := NewEnforcerBuilder().Build()
require.NoError(t, err)
Expand Down
1 change: 1 addition & 0 deletions api/service/projects_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func (service *projectsService) updateAuthorizationPolicy(ctx context.Context, p
return service.authEnforcer.UpdateAuthorization(ctx, updateRequest)
}

// TODO: Evaluate if we should retrieve all permissions granted to a user as opposed to just roles
func (service *projectsService) filterAuthorizedProjects(ctx context.Context, projects []*models.Project,
user string) ([]*models.Project, error) {
if user == "" {
Expand Down

0 comments on commit 776ba32

Please sign in to comment.