Add integration tests for record ctx roles
This commit is contained in:
@@ -837,7 +837,7 @@ func initRoles(ctx context.Context, log *zap.Logger, opt options.RBACOpt, eb eve
|
||||
DefaultRole.SetClosed(tmp...)
|
||||
|
||||
// Initial RBAC update
|
||||
if err = updateRbacRoles(ctx, log, ru, bypass, authenticated, anonymous); err != nil {
|
||||
if err = UpdateRbacRoles(ctx, log, ru, bypass, authenticated, anonymous); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -846,7 +846,7 @@ func initRoles(ctx context.Context, log *zap.Logger, opt options.RBACOpt, eb eve
|
||||
eb.Register(
|
||||
func(_ context.Context, ev eventbus.Event) error {
|
||||
log.Debug("role changed, updating RBAC")
|
||||
return updateRbacRoles(ctx, log, ru, bypass, authenticated, anonymous)
|
||||
return UpdateRbacRoles(ctx, log, ru, bypass, authenticated, anonymous)
|
||||
},
|
||||
eventbus.For("system:role"),
|
||||
eventbus.On("afterUpdate", "afterCreate", "afterDelete"),
|
||||
@@ -855,7 +855,7 @@ func initRoles(ctx context.Context, log *zap.Logger, opt options.RBACOpt, eb eve
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRbacRoles(ctx context.Context, log *zap.Logger, ru rbacRoleUpdater, bypass, authenticated, anonymous map[string]bool) error {
|
||||
func UpdateRbacRoles(ctx context.Context, log *zap.Logger, ru rbacRoleUpdater, bypass, authenticated, anonymous map[string]bool) error {
|
||||
var (
|
||||
p = expr.NewParser()
|
||||
f = types.RoleFilter{}
|
||||
|
||||
@@ -111,14 +111,18 @@ func newHelper(t *testing.T) helper {
|
||||
|
||||
h.cUser.SetRoles(h.roleID)
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
h.identityToHelper(h.cUser)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *helper) identityToHelper(u *sysTypes.User) {
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser)
|
||||
h.cUser = u
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h helper) MyRole() uint64 {
|
||||
@@ -184,7 +188,7 @@ func cleanup(t *testing.T) {
|
||||
}
|
||||
|
||||
func loadScenario(ctx context.Context, s store.Storer, t *testing.T, h helper) {
|
||||
loadScenarioWithName(ctx, s, t, h, "S"+t.Name()[4:])
|
||||
loadScenarioWithName(ctx, s, t, h, t.Name()[5:])
|
||||
}
|
||||
|
||||
func loadScenarioWithName(ctx context.Context, s store.Storer, t *testing.T, h helper, scenario string) {
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ func findPageByHandle(pp types.PageSet, h string) *types.Page {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test0001_namespace_duplicate(t *testing.T) {
|
||||
func Test_namespace_duplicate(t *testing.T) {
|
||||
ctx, h, s := setup(t)
|
||||
loadScenario(ctx, defStore, t, h)
|
||||
ns, _, _, _, _, err := fetchEntireNamespace(ctx, s, "ns1")
|
||||
@@ -0,0 +1,154 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
systemTypes "github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func crissCrossUserRoles(ctx context.Context, s store.Storer, h helper, uu systemTypes.UserSet, rr systemTypes.RoleSet) (map[string]*systemTypes.User, map[string]*systemTypes.Role) {
|
||||
ux := make(map[string]*systemTypes.User)
|
||||
for _, r := range uu {
|
||||
ux[r.Name] = r
|
||||
}
|
||||
|
||||
rx := make(map[string]*systemTypes.Role)
|
||||
for _, r := range rr {
|
||||
rx[r.Handle] = r
|
||||
}
|
||||
|
||||
for _, u := range uu {
|
||||
if _, ok := rx[u.Name]; !ok {
|
||||
h.a.FailNow(fmt.Sprintf("corresponding role not found for user: r: %s; u: %s", u.Name, u.Handle))
|
||||
}
|
||||
rID := rx[u.Name].ID
|
||||
h.a.NoError(store.CreateRoleMember(ctx, s, &systemTypes.RoleMember{
|
||||
RoleID: rID,
|
||||
UserID: u.ID,
|
||||
}))
|
||||
u.SetRoles(rID)
|
||||
}
|
||||
|
||||
return ux, rx
|
||||
}
|
||||
|
||||
func Test_record_access_context(t *testing.T) {
|
||||
ctx, h, s := setup(t)
|
||||
loadScenario(ctx, defStore, t, h)
|
||||
|
||||
// setup
|
||||
rr, _, err := store.SearchRoles(ctx, s, systemTypes.RoleFilter{})
|
||||
h.a.NoError(err)
|
||||
uu, _, err := store.SearchUsers(ctx, s, systemTypes.UserFilter{})
|
||||
h.a.NoError(err)
|
||||
|
||||
ux, rx := crissCrossUserRoles(ctx, s, h, uu, rr)
|
||||
|
||||
ns, err := store.LookupComposeNamespaceBySlug(ctx, s, "ns1")
|
||||
h.a.NoError(err)
|
||||
|
||||
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, ns.ID, "mod1")
|
||||
h.a.NoError(err)
|
||||
|
||||
records, _, err := store.SearchComposeRecords(ctx, s, mod, types.RecordFilter{})
|
||||
h.a.NoError(err)
|
||||
rec := records[0]
|
||||
|
||||
for _, r := range rr {
|
||||
helpers.Allow(r, types.NamespaceRbacResource(0), "read")
|
||||
helpers.Allow(r, types.ModuleRbacResource(0, 0), "read")
|
||||
}
|
||||
|
||||
helpers.DenyMe(h, rec.RbacResource(), "read")
|
||||
helpers.Allow(rx["owner"], rec.RbacResource(), "read")
|
||||
helpers.Allow(rx["creator"], rec.RbacResource(), "read")
|
||||
helpers.Allow(rx["updater"], rec.RbacResource(), "read")
|
||||
helpers.Allow(rx["deleter"], rec.RbacResource(), "read")
|
||||
|
||||
h.a.NoError(service.UpdateRbacRoles(ctx, testApp.Log, rbac.Global(), nil, nil, nil))
|
||||
rbac.Global().Reload(ctx)
|
||||
|
||||
t.Run("generic user with no ctx role", func(t *testing.T) {
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertError("record.errors.notAllowedToRead")).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with owner ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["owner"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with creator ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["creator"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with updater ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["updater"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with deleter ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["deleter"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with creator ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["creator"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
|
||||
t.Run("user with creator ctx role", func(t *testing.T) {
|
||||
h.identityToHelper(ux["creator"])
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/namespace/%d/module/%d/record/%d", mod.NamespaceID, mod.ID, rec.ID)).
|
||||
Header("Accept", "application/json").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespaces:
|
||||
ns1:
|
||||
name: ns1 name
|
||||
|
||||
modules:
|
||||
mod1:
|
||||
name: mod1 name
|
||||
fields:
|
||||
f1:
|
||||
label: f1 label
|
||||
kind: String
|
||||
required: true
|
||||
f2:
|
||||
label: f2 label
|
||||
kind: Select
|
||||
options:
|
||||
options:
|
||||
- f2 opt 1
|
||||
- f2 opt 2
|
||||
- f2 opt 3
|
||||
|
||||
records:
|
||||
mod1:
|
||||
- createdBy: user_createdby
|
||||
updatedBy: user_updatedby
|
||||
deletedBy: user_deletedby
|
||||
ownedBy: user_ownedby
|
||||
values:
|
||||
f1: "mod1 f1 v1"
|
||||
f2: "f2 opt 1"
|
||||
@@ -0,0 +1,49 @@
|
||||
users:
|
||||
user_createdby:
|
||||
email: user_createdby@test.tld
|
||||
name: creator
|
||||
|
||||
user_updatedby:
|
||||
email: user_updatedby@test.tld
|
||||
name: updater
|
||||
|
||||
user_deletedby:
|
||||
email: user_deletedby@test.tld
|
||||
name: deleter
|
||||
|
||||
user_ownedby:
|
||||
email: user_ownedby@test.tld
|
||||
name: owner
|
||||
|
||||
roles:
|
||||
owner:
|
||||
name: Owner
|
||||
meta:
|
||||
context:
|
||||
expr: resource.ownedBy == userID
|
||||
resourceType:
|
||||
- corteza::compose:record
|
||||
|
||||
creator:
|
||||
name: Creator
|
||||
meta:
|
||||
context:
|
||||
expr: resource.createdBy == userID
|
||||
resourceType:
|
||||
- corteza::compose:record
|
||||
|
||||
updater:
|
||||
name: Updater
|
||||
meta:
|
||||
context:
|
||||
expr: resource.updatedBy == userID
|
||||
resourceType:
|
||||
- corteza::compose:record
|
||||
|
||||
deleter:
|
||||
name: Deleter
|
||||
meta:
|
||||
context:
|
||||
expr: resource.deletedBy == userID
|
||||
resourceType:
|
||||
- corteza::compose:record
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -41,12 +42,24 @@ func AllowMe(mrg myRoleGetter, r string, oo ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
func Allow(role *types.Role, r string, oo ...string) {
|
||||
for _, o := range oo {
|
||||
Grant(rbac.AllowRule(role.ID, r, o))
|
||||
}
|
||||
}
|
||||
|
||||
func DenyMe(mrg myRoleGetter, r string, oo ...string) {
|
||||
for _, o := range oo {
|
||||
Grant(rbac.DenyRule(mrg.MyRole(), r, o))
|
||||
}
|
||||
}
|
||||
|
||||
func Deny(role *types.Role, r string, oo ...string) {
|
||||
for _, o := range oo {
|
||||
Grant(rbac.DenyRule(role.ID, r, o))
|
||||
}
|
||||
}
|
||||
|
||||
func Grant(rr ...*rbac.Rule) {
|
||||
cli.HandleError(rbac.Global().Grant(context.Background(), rr...))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user