Update role definition for casbin

This commit is contained in:
Abin Simon
2022-03-22 13:40:46 +05:30
parent 54fc074d7d
commit 1dce43d607
6 changed files with 123 additions and 116 deletions

View File

@@ -3,6 +3,7 @@ package authv3
import (
"os"
"github.com/RafayLabs/rcloud-base/pkg/enforcer"
logv2 "github.com/RafayLabs/rcloud-base/pkg/log"
"github.com/RafayLabs/rcloud-base/pkg/service"
kclient "github.com/ory/kratos-client-go"

View File

@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"strconv"
"strings"
"time"
"github.com/RafayLabs/rcloud-base/internal/dao"
@@ -77,53 +77,75 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
// TODO: add transactions
projectNamespaceRoles := group.GetSpec().GetProjectNamespaceRoles()
var pgnrs []models.ProjectGroupNamespaceRole
var pgrs []models.ProjectGroupRole
var grs []models.GroupRole
var ps []*authzv1.Policy
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetIdByName(ctx, db, role, &models.Role{})
entity, err := pg.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
var scope string
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
scope = strings.ToLower(rle.Scope)
} else {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
org := group.GetMetadata().GetOrganization()
namespaceId := pnr.GetNamespace() // TODO: lookup id from name
switch {
case namespaceId != 0:
projectId, err := dao.GetProjectId(ctx, db, project)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
}
pgnr := models.ProjectGroupNamespaceRole{
switch scope {
case "system":
gr := models.GroupRole{
Trash: false,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization,
GroupId: ids.Id,
ProjectId: projectId,
NamespaceId: namespaceId,
Active: true,
}
pgnrs = append(pgnrs, pgnr)
grs = append(grs, gr)
ps = append(ps, &authzv1.Policy{
Sub: "g:" + group.GetMetadata().GetName(),
Ns: strconv.FormatInt(namespaceId, 10),
Proj: project,
Ns: "*",
Proj: "*",
Org: "*",
Obj: role,
})
case "organization":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
}
gr := models.GroupRole{
Trash: false,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization,
GroupId: ids.Id,
Active: true,
}
grs = append(grs, gr)
ps = append(ps, &authzv1.Policy{
Sub: "g:" + group.GetMetadata().GetName(),
Ns: "*",
Proj: "*",
Org: org,
Obj: role,
})
case project != "":
projectId, err := dao.GetProjectId(ctx, db, project)
case "project":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.Group{}, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := pg.GetProjectId(ctx, s.db, project)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
}
@@ -145,29 +167,6 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
Org: org,
Obj: role,
})
default:
gr := models.GroupRole{
Trash: false,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization,
GroupId: ids.Id,
Active: true,
}
grs = append(grs, gr)
ps = append(ps, &authzv1.Policy{
Sub: "g:" + group.GetMetadata().GetName(),
Ns: "*",
Proj: "*",
Org: org,
Obj: role,
})
}
}
if len(pgnrs) > 0 {
_, err := dao.Create(ctx, db, &pgnrs)
if err != nil {
return &userv3.Group{}, err
}
}
if len(pgrs) > 0 {

View File

@@ -216,9 +216,10 @@ func TestCreateGroupNoUsersWithRoles(t *testing.T) {
name string
roles []*userv3.ProjectNamespaceRole
dbname string
scope string
shouldfail bool
}{
{"just role", []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", false},
{"just role", []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system", false},
// {"just project", []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", true}, // no role creation without role
// {"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
// {"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
@@ -249,8 +250,8 @@ func TestCreateGroupNoUsersWithRoles(t *testing.T) {
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO "authsrv_group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
@@ -295,14 +296,15 @@ func TestCreateGroupWithUsersWithRoles(t *testing.T) {
users []string
roles []*userv3.ProjectNamespaceRole
dbname string
scope string
shouldfail bool
}{
{"just role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", false},
{"just project", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", true}, // no role creation without role
{"just namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
{"project and namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
{"project and role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Role: uuid.New().String()}}, "authsrv_projectgrouprole", false},
{"project role namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid, Role: uuid.New().String()}}, "authsrv_projectgroupnamespacerole", false},
{"just role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system", false},
{"just project", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", "system", true}, // no role creation without role
{"just namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_projectgrouprole", "project", true}, // no role creation without role,
{"project and namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", "project", true}, // no role creation without role,
{"project and role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Role: uuid.New().String()}}, "authsrv_projectgrouprole", "project", false},
// {"project role namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid, Role: uuid.New().String()}}, "authsrv_projectgroupnamespacerole", false},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@@ -334,8 +336,8 @@ func TestCreateGroupWithUsersWithRoles(t *testing.T) {
mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
@@ -383,8 +385,9 @@ func TestUpdateGroupWithUsersWithRoles(t *testing.T) {
users []string
roles []*userv3.ProjectNamespaceRole
dbname string
scope string
}{
{"user role update", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole"},
{"user role update", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system"},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@@ -422,8 +425,8 @@ func TestUpdateGroupWithUsersWithRoles(t *testing.T) {
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))

View File

@@ -131,10 +131,7 @@ func (s *roleService) Create(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
}
scope := role.GetSpec().GetScope()
// since this is purely additional metadata at this point, we
// can kinda treat it as optional, and so we are allowing empty
// TODO: check if "" is valid
if !contains([]string{"system", "organization", "project", ""}, strings.ToLower(scope)) {
if !contains([]string{"system", "organization", "project"}, strings.ToLower(scope)) {
return nil, fmt.Errorf("unknown scope '%v'", scope)
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"strconv"
"strings"
"time"
"github.com/google/uuid"
@@ -102,56 +102,84 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
projectNamespaceRoles := user.GetSpec().GetProjectNamespaceRoles()
// TODO: add transactions
var panrs []models.ProjectAccountNamespaceRole
var pars []models.ProjectAccountResourcerole
var ars []models.AccountResourcerole
var ps []*authzv1.Policy
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetIdByName(ctx, db, role, &models.Role{})
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return user, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
var scope string
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
scope = strings.ToLower(rle.Scope)
} else {
return user, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
org := user.GetMetadata().GetOrganization()
namespaceId := pnr.GetNamespace() // TODO: lookup id from name
switch {
case pnr.Namespace != nil:
projectId, err := dao.GetProjectId(ctx, db, project)
if err != nil {
return user, fmt.Errorf("unable to find project '%v'", project)
}
panr := models.ProjectAccountNamespaceRole{
switch scope {
case "system":
ar := models.AccountResourcerole{
CreatedAt: time.Now(),
ModifiedAt: time.Now(),
Trash: false,
Default: true,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization, // Not really used
AccountId: ids.Id,
Active: true,
}
ars = append(ars, ar)
ps = append(ps, &authzv1.Policy{
Sub: "u:" + user.GetMetadata().GetName(),
Ns: "*",
Proj: "*",
Org: "*",
Obj: role,
})
case "organization":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
}
ar := models.AccountResourcerole{
CreatedAt: time.Now(),
ModifiedAt: time.Now(),
Trash: false,
Default: true,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization,
AccountId: ids.Id,
ProjectId: projectId,
NamespaceId: namespaceId,
Active: true,
}
panrs = append(panrs, panr)
ars = append(ars, ar)
ps = append(ps, &authzv1.Policy{
Sub: "u:" + user.GetMetadata().GetName(),
Ns: strconv.FormatInt(namespaceId, 10),
Proj: project,
Ns: "*",
Proj: "*",
Org: org,
Obj: role,
})
case project != "":
projectId, err := dao.GetProjectId(ctx, db, project)
case "project":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.User{}, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := pg.GetProjectId(ctx, db, project)
if err != nil {
return user, fmt.Errorf("unable to find project '%v'", project)
}
@@ -177,32 +205,9 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
Obj: role,
})
default:
ar := models.AccountResourcerole{
CreatedAt: time.Now(),
ModifiedAt: time.Now(),
Trash: false,
Default: true,
RoleId: roleId,
PartnerId: ids.Partner,
OrganizationId: ids.Organization,
AccountId: ids.Id,
Active: true,
if err != nil {
return user, fmt.Errorf("namespace specific roles are not handled")
}
ars = append(ars, ar)
ps = append(ps, &authzv1.Policy{
Sub: "u:" + user.GetMetadata().GetName(),
Ns: "*",
Proj: "*",
Org: org,
Obj: role,
})
}
}
if len(panrs) > 0 {
_, err := dao.Create(ctx, db, &panrs)
if err != nil {
return &userv3.User{}, err
}
}
if len(pars) > 0 {

View File

@@ -99,14 +99,16 @@ func TestCreateUserWithRole(t *testing.T) {
name string
roles []*userv3.ProjectNamespaceRole
dbname string
scope string
shouldfail bool
}{
{"just role", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", false},
{"just project", []*userv3.ProjectNamespaceRole{{Project: &prname}}, "authsrv_accountrole", true}, // no role creation without role
{"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_accountrole", true}, // no role creation without role,
{"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid}}, "authsrv_accountrole", true}, // no role creation without role,
{"project and role", []*userv3.ProjectNamespaceRole{{Project: &prname, Role: rname}}, "authsrv_projectaccountresourcerole", false},
{"project role namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}, "authsrv_projectaccountnamespacerole", false},
{"just role", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "system", false},
{"just role org scope", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "organization", false},
{"just project", []*userv3.ProjectNamespaceRole{{Project: &prname}}, "authsrv_accountrole", "system", true}, // no role creation without role
{"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and role", []*userv3.ProjectNamespaceRole{{Project: &prname, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
{"project role namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
}
for _, tc := range tt {
@@ -129,8 +131,8 @@ func TestCreateUserWithRole(t *testing.T) {
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectBegin()
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
@@ -198,11 +200,11 @@ func TestUpdateUser(t *testing.T) {
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", "project"))
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountnamespacerole"`).
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountresourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectCommit()