Skip to content

Commit

Permalink
feat: update role permissions in place
Browse files Browse the repository at this point in the history
Signed-off-by: Kush Sharma <[email protected]>
  • Loading branch information
kushsharma committed Aug 16, 2023
1 parent 2cae175 commit 2e52934
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 71 deletions.
4 changes: 2 additions & 2 deletions core/role/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ type Repository interface {
Get(ctx context.Context, id string) (Role, error)
GetByName(ctx context.Context, orgID, name string) (Role, error)
List(ctx context.Context, f Filter) ([]Role, error)
Upsert(ctx context.Context, role Role) (string, error)
Update(ctx context.Context, toUpdate Role) (string, error)
Upsert(ctx context.Context, role Role) (Role, error)
Update(ctx context.Context, toUpdate Role) (Role, error)
Delete(ctx context.Context, roleID string) error
}

Expand Down
85 changes: 77 additions & 8 deletions core/role/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,27 @@ func (s Service) Upsert(ctx context.Context, toCreate Role) (Role, error) {
}
}

roleID, err := s.repository.Upsert(ctx, toCreate)
createdRole, err := s.repository.Upsert(ctx, toCreate)
if err != nil {
return Role{}, err
}

// create relation between role and permissions
if err := s.createRolePermissionRelation(ctx, createdRole.ID, createdRole.Permissions); err != nil {
return Role{}, err
}

return createdRole, nil
}

func (s Service) createRolePermissionRelation(ctx context.Context, roleID string, permissions []string) error {
// create relation between role and permissions
// for example for each permission:
// app/role:org_owner#organization_delete@app/user:*
// app/role:org_owner#organization_update@app/user:*
// this needs to be created for each type of principles
for _, perm := range toCreate.Permissions {
_, err = s.relationService.Create(ctx, relation.Relation{
for _, perm := range permissions {
_, err := s.relationService.Create(ctx, relation.Relation{
Object: relation.Object{
ID: roleID,
Namespace: schema.RoleNamespace,
Expand All @@ -67,7 +76,7 @@ func (s Service) Upsert(ctx context.Context, toCreate Role) (Role, error) {
RelationName: perm,
})
if err != nil {
return Role{}, err
return err
}
// do the same with service user
_, err = s.relationService.Create(ctx, relation.Relation{
Expand All @@ -82,11 +91,50 @@ func (s Service) Upsert(ctx context.Context, toCreate Role) (Role, error) {
RelationName: perm,
})
if err != nil {
return Role{}, err
return err
}
}
return nil
}

return s.repository.Get(ctx, roleID)
func (s Service) deleteRolePermissionRelation(ctx context.Context, roleID string, permissions []string) error {
// delete relation between role and permissions
// for example for each permission:
// app/role:org_owner#organization_delete@app/user:*
// app/role:org_owner#organization_update@app/user:*
// this needs to be created for each type of principles
for _, perm := range permissions {
err := s.relationService.Delete(ctx, relation.Relation{
Object: relation.Object{
ID: roleID,
Namespace: schema.RoleNamespace,
},
Subject: relation.Subject{
ID: "*", // all principles who have role will have access
Namespace: schema.UserPrincipal,
},
RelationName: perm,
})
if err != nil {
return err
}
// do the same with service user
err = s.relationService.Delete(ctx, relation.Relation{
Object: relation.Object{
ID: roleID,
Namespace: schema.RoleNamespace,
},
Subject: relation.Subject{
ID: "*", // all principles who have role will have access
Namespace: schema.ServiceUserPrincipal,
},
RelationName: perm,
})
if err != nil {
return err
}
}
return nil
}

func (s Service) Get(ctx context.Context, id string) (Role, error) {
Expand All @@ -111,11 +159,32 @@ func (s Service) Update(ctx context.Context, toUpdate Role) (Role, error) {
}
}

roleID, err := s.repository.Update(ctx, toUpdate)
// fetch existing role
existingRole, err := s.Get(ctx, toUpdate.ID)
if err != nil {
return Role{}, err
}
return s.repository.Get(ctx, roleID)

// figure out what to delete from permission relation
var permissionsToDelete []string
for _, perm := range existingRole.Permissions {
if !utils.Contains(toUpdate.Permissions, perm) {
permissionsToDelete = append(permissionsToDelete, perm)
}
}

// delete relation between role and permissions
if err := s.deleteRolePermissionRelation(ctx, existingRole.ID, permissionsToDelete); err != nil {
return Role{}, err
}

// create relation between role and permissions
if err := s.createRolePermissionRelation(ctx, existingRole.ID, toUpdate.Permissions); err != nil {
return Role{}, err
}

// update in db
return s.repository.Update(ctx, toUpdate)
}

func (s Service) Delete(ctx context.Context, id string) error {
Expand Down
4 changes: 4 additions & 0 deletions internal/api/v1beta1/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"

"github.com/raystack/frontier/core/role"

"github.com/raystack/frontier/core/audit"
"github.com/raystack/frontier/internal/bootstrap/schema"

Expand Down Expand Up @@ -91,6 +93,8 @@ func (h Handler) CreatePolicy(ctx context.Context, request *frontierv1beta1.Crea
if err != nil {
logger.Error(err.Error())
switch {
case errors.Is(err, role.ErrInvalidID):
return nil, status.Error(codes.InvalidArgument, err.Error())
case errors.Is(err, policy.ErrInvalidDetail):
return nil, grpcBadBodyError
default:
Expand Down
2 changes: 2 additions & 0 deletions internal/store/postgres/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,7 @@ func (from Policy) transformToPolicy() (policy.Policy, error) {
PrincipalID: from.PrincipalID,
PrincipalType: from.PrincipalType,
Metadata: unmarshalledMetadata,
CreatedAt: from.CreatedAt,
UpdatedAt: from.UpdatedAt,
}, nil
}
20 changes: 11 additions & 9 deletions internal/store/postgres/policy_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"testing"

"github.com/raystack/frontier/core/role"

"github.com/raystack/frontier/internal/bootstrap/schema"

"github.com/google/go-cmp/cmp"
Expand All @@ -30,7 +32,7 @@ type PolicyRepositoryTestSuite struct {
policyIDs []string
userID string
orgID string
roleID string
role role.Role
}

func (s *PolicyRepositoryTestSuite) SetupSuite() {
Expand Down Expand Up @@ -65,7 +67,7 @@ func (s *PolicyRepositoryTestSuite) SetupSuite() {
if err != nil {
s.T().Fatal(err)
}
s.roleID = roles[0]
s.role = roles[0]

users, err := bootstrapUser(s.client)
if err != nil {
Expand All @@ -76,7 +78,7 @@ func (s *PolicyRepositoryTestSuite) SetupSuite() {

func (s *PolicyRepositoryTestSuite) SetupTest() {
var err error
s.policyIDs, err = bootstrapPolicy(s.client, s.orgID, s.roleID, s.userID)
s.policyIDs, err = bootstrapPolicy(s.client, s.orgID, s.role, s.userID)
if err != nil {
s.T().Fatal(err)
}
Expand Down Expand Up @@ -115,7 +117,7 @@ func (s *PolicyRepositoryTestSuite) TestGet() {
Description: "should get a policy",
SelectedID: s.policyIDs[0],
ExpectedPolicy: policy.Policy{
RoleID: s.roleID,
RoleID: s.role.ID,
ResourceType: "ns1",
PrincipalID: s.userID,
PrincipalType: schema.UserPrincipal,
Expand Down Expand Up @@ -167,7 +169,7 @@ func (s *PolicyRepositoryTestSuite) TestCreate() {
{
Description: "should create a policy",
PolicyToCreate: policy.Policy{
RoleID: s.roleID,
RoleID: s.role.ID,
ResourceID: uuid.NewString(),
ResourceType: "ns1",
PrincipalID: s.userID,
Expand All @@ -185,7 +187,7 @@ func (s *PolicyRepositoryTestSuite) TestCreate() {
{
Description: "should return error if namespace id does not exist",
PolicyToCreate: policy.Policy{
RoleID: s.roleID,
RoleID: s.role.ID,
ResourceType: "ns1-random",
},
Err: policy.ErrInvalidDetail,
Expand Down Expand Up @@ -221,13 +223,13 @@ func (s *PolicyRepositoryTestSuite) TestList() {
Description: "should get all policies",
ExpectedPolicys: []policy.Policy{
{
RoleID: s.roleID,
RoleID: s.role.ID,
PrincipalID: s.userID,
ResourceID: s.orgID,
ResourceType: "ns1",
},
{
RoleID: s.roleID,
RoleID: s.role.ID,
PrincipalID: s.userID,
ResourceID: s.orgID,
ResourceType: "ns2",
Expand Down Expand Up @@ -270,7 +272,7 @@ func (s *PolicyRepositoryTestSuite) TestUpdate() {
Description: "should update an policy",
PolicyToUpdate: policy.Policy{
ID: s.policyIDs[0],
RoleID: s.roleID,
RoleID: s.role.ID,
ResourceType: "ns1",
},
ExpectedPolicyID: s.policyIDs[0],
Expand Down
8 changes: 4 additions & 4 deletions internal/store/postgres/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func bootstrapUser(client *db.Client) ([]user.User, error) {
return insertedData, nil
}

func bootstrapRole(client *db.Client, orgID string) ([]string, error) {
func bootstrapRole(client *db.Client, orgID string) ([]role.Role, error) {
roleRepository := postgres.NewRoleRepository(client)
testFixtureJSON, err := os.ReadFile("./testdata/mock-role.json")
if err != nil {
Expand All @@ -247,7 +247,7 @@ func bootstrapRole(client *db.Client, orgID string) ([]string, error) {
return nil, err
}

var insertedData []string
var insertedData []role.Role
for _, d := range data {
d.OrgID = orgID
domain, err := roleRepository.Upsert(context.Background(), d)
Expand All @@ -261,7 +261,7 @@ func bootstrapRole(client *db.Client, orgID string) ([]string, error) {
return insertedData, nil
}

func bootstrapPolicy(client *db.Client, orgID, roleID, userID string) ([]string, error) {
func bootstrapPolicy(client *db.Client, orgID string, role role.Role, userID string) ([]string, error) {
policyRepository := postgres.NewPolicyRepository(client)
testFixtureJSON, err := os.ReadFile("./testdata/mock-policy.json")
if err != nil {
Expand All @@ -277,7 +277,7 @@ func bootstrapPolicy(client *db.Client, orgID, roleID, userID string) ([]string,
for _, d := range data {
d.PrincipalID = userID
d.ResourceID = orgID
d.RoleID = roleID
d.RoleID = role.ID
domain, err := policyRepository.Upsert(context.Background(), d)
if err != nil {
return nil, err
Expand Down
18 changes: 10 additions & 8 deletions internal/store/postgres/role.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package postgres

import (
"database/sql"
"encoding/json"
"time"

"github.com/raystack/frontier/core/role"
)

type Role struct {
ID string `db:"id"`
OrgID string `db:"org_id"`
Name string `db:"name"`
Permissions []byte `db:"permissions"`
State string `db:"state"`
Metadata []byte `db:"metadata"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID string `db:"id"`
OrgID string `db:"org_id"`
Name string `db:"name"`
Permissions []byte `db:"permissions"`
State string `db:"state"`
Metadata []byte `db:"metadata"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt sql.NullTime `db:"deleted_at"`
}

func (from Role) transformToRole() (role.Role, error) {
Expand Down
Loading

0 comments on commit 2e52934

Please sign in to comment.