Permission testing
This commit is contained in:
@@ -175,12 +175,16 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return ErrNoGrantPermissions
|
||||
}
|
||||
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
}
|
||||
|
||||
func (svc accessControl) FindRulesByRoleID(ctx context.Context, roleID uint64) (permissions.RuleSet, error) {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return nil, ErrNoGrantPermissions
|
||||
return nil, ErrNoPermissions
|
||||
}
|
||||
|
||||
return svc.permissions.FindRulesByRoleID(roleID), nil
|
||||
|
||||
@@ -11,6 +11,7 @@ type (
|
||||
const (
|
||||
ErrInvalidID serviceError = "InvalidID"
|
||||
ErrStaleData serviceError = "StaleData"
|
||||
ErrNoPermissions serviceError = "NoPermissions"
|
||||
ErrNoGrantPermissions serviceError = "NoGrantPermissions"
|
||||
ErrNoCreatePermissions serviceError = "NoCreatePermissions"
|
||||
ErrNoReadPermissions serviceError = "NoReadPermissions"
|
||||
|
||||
@@ -30,6 +30,8 @@ type (
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultStore store.Store
|
||||
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
// DefaultPermissions Retrieves & stores permissions
|
||||
@@ -64,19 +66,23 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
|
||||
|
||||
DefaultLogger = log.Named("service")
|
||||
|
||||
fs, err := store.New(c.Storage.Path)
|
||||
log.Info("initializing store", zap.String("path", c.Storage.Path), zap.Error(err))
|
||||
if err != nil {
|
||||
return err
|
||||
if DefaultStore == nil {
|
||||
DefaultStore, err = store.New(c.Storage.Path)
|
||||
log.Info("initializing store", zap.String("path", c.Storage.Path), zap.Error(err))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Permissions, access control
|
||||
DefaultPermissions = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(db, "compose_permission_rules"))
|
||||
|
||||
if DefaultPermissions == nil {
|
||||
DefaultPermissions = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(db, "compose_permission_rules"))
|
||||
}
|
||||
DefaultAccessControl = AccessControl(DefaultPermissions)
|
||||
|
||||
DefaultNamespace = Namespace()
|
||||
DefaultModule = Module()
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package permissions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -49,6 +51,21 @@ func (svc *TestService) ClearGrants() {
|
||||
svc.rules = RuleSet{}
|
||||
}
|
||||
|
||||
func (svc *TestService) String() (out string) {
|
||||
tpl := "%20v\t%-30s\t%-30s\t%v\n"
|
||||
out = fmt.Sprintf(tpl, "role", "res", "op", "access")
|
||||
out += strings.Repeat("-", 120) + "\n"
|
||||
|
||||
_ = svc.rules.Walk(func(r *Rule) error {
|
||||
out += fmt.Sprintf(tpl, r.RoleID, r.Resource, r.Operation, r.Access)
|
||||
return nil
|
||||
})
|
||||
|
||||
out += strings.Repeat("-", 120) + "\n"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func NewTestService() *TestService {
|
||||
return &TestService{
|
||||
service: service{},
|
||||
|
||||
@@ -215,6 +215,10 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
}
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return ErrNoGrantPermissions
|
||||
}
|
||||
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
ErrInvalidID serviceError = "InvalidID"
|
||||
ErrNoPermissions serviceError = "NoPermissions"
|
||||
ErrInvalidID serviceError = "InvalidID"
|
||||
ErrNoPermissions serviceError = "NoPermissions"
|
||||
ErrNoGrantPermissions serviceError = "NoGrantPermissions"
|
||||
)
|
||||
|
||||
func (e serviceError) Error() string {
|
||||
|
||||
@@ -35,12 +35,12 @@ func (svc accessControl) Effective(ctx context.Context) (ee permissions.Effectiv
|
||||
ee = permissions.EffectiveSet{}
|
||||
|
||||
ee.Push(types.SystemPermissionResource, "access", svc.CanAccess(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "grant", svc.CanGrant(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "settings.read", svc.CanReadSettings(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "settings.manage", svc.CanManageSettings(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "application.create", svc.CanCreateApplication(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "role.create", svc.CanCreateRole(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "organisation.create", svc.CanCreateOrganisation(ctx))
|
||||
ee.Push(types.SystemPermissionResource, "grant", svc.CanCreateRole(ctx))
|
||||
|
||||
return
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func (svc accessControl) can(ctx context.Context, res permissionResource, op per
|
||||
|
||||
func (svc accessControl) Grant(ctx context.Context, rr ...*permissions.Rule) error {
|
||||
if !svc.CanGrant(ctx) {
|
||||
return ErrNoPermissions
|
||||
return ErrNoGrantPermissions
|
||||
}
|
||||
|
||||
return svc.permissions.Grant(ctx, svc.Whitelist(), rr...)
|
||||
|
||||
@@ -69,10 +69,11 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
|
||||
|
||||
DefaultIntSettings = internalSettings.NewService(internalSettings.NewRepository(repository.DB(ctx), "sys_settings"))
|
||||
|
||||
DefaultPermissions = permissions.Service(
|
||||
ctx,
|
||||
DefaultLogger,
|
||||
permissions.Repository(repository.DB(ctx), "sys_permission_rules"))
|
||||
if DefaultPermissions == nil {
|
||||
pRepo := permissions.Repository(repository.DB(ctx), "sys_permission_rules")
|
||||
DefaultPermissions = permissions.Service(ctx, DefaultLogger, pRepo)
|
||||
}
|
||||
|
||||
DefaultAccessControl = AccessControl(DefaultPermissions)
|
||||
|
||||
DefaultSettings = Settings(ctx, DefaultIntSettings)
|
||||
|
||||
@@ -6,29 +6,39 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/permissions"
|
||||
"github.com/cortezaproject/corteza-server/messaging/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.a.Empty(p.FindRulesByRoleID(h.roleID))
|
||||
// Make sure our user can grant
|
||||
h.allow(types.MessagingPermissionResource, "grant")
|
||||
|
||||
h.allow("messaging", "access")
|
||||
h.allow("messaging", "grant")
|
||||
h.deny("messaging", "channel.group.create")
|
||||
// New role.
|
||||
permDelRole := h.roleID + 1
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(h.roleID), 3)
|
||||
h.a.Len(p.FindRulesByRoleID(permDelRole), 0)
|
||||
|
||||
// Setup a few fake rules for new roke
|
||||
h.mockPermissions(
|
||||
permissions.AllowRule(permDelRole, types.MessagingPermissionResource, "access"),
|
||||
permissions.DenyRule(permDelRole, types.MessagingPermissionResource, "channel.group.create"),
|
||||
permissions.DenyRule(permDelRole, types.MessagingPermissionResource, "channel.private.create"),
|
||||
)
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(permDelRole), 3)
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Make sure everything is deleted
|
||||
rr, _ := p.FindRulesByRoleID(h.roleID).Filter(func(r *permissions.Rule) (b bool, e error) {
|
||||
rr, _ := p.FindRulesByRoleID(permDelRole).Filter(func(r *permissions.Rule) (b bool, e error) {
|
||||
return r.Access != permissions.Inherit, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@ import (
|
||||
|
||||
func TestPermissionsUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.allow("messaging", "access")
|
||||
h.allow("messaging", "grant")
|
||||
h.deny("messaging", "channel.group.create")
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestApplicationCreate(t *testing.T) {
|
||||
t.Skip("to be implemented")
|
||||
}
|
||||
|
||||
func TestApplicationRead(t *testing.T) {
|
||||
t.Skip("to be implemented")
|
||||
}
|
||||
|
||||
func TestApplicationUpdate(t *testing.T) {
|
||||
t.Skip("to be implemented")
|
||||
}
|
||||
|
||||
func TestApplicationDelete(t *testing.T) {
|
||||
t.Skip("to be implemented")
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/steinfletcher/apitest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/titpetric/factory"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/auth"
|
||||
"github.com/cortezaproject/corteza-server/internal/permissions"
|
||||
"github.com/cortezaproject/corteza-server/internal/rand"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/system"
|
||||
"github.com/cortezaproject/corteza-server/system/rest"
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
type (
|
||||
helper struct {
|
||||
t *testing.T
|
||||
a *require.Assertions
|
||||
|
||||
cUser *types.User
|
||||
roleID uint64
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
cfg *cli.Config
|
||||
r chi.Router
|
||||
p = permissions.NewTestService()
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
var err error
|
||||
|
||||
if cfg != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helpers.RecursiveDotEnvLoad()
|
||||
|
||||
ctx := context.Background()
|
||||
log, _ := zap.NewDevelopment()
|
||||
|
||||
cfg = system.Configure()
|
||||
cfg.Log = log
|
||||
|
||||
cfg.Init()
|
||||
|
||||
auth.SetupDefault(string(rand.Bytes(32)), 10)
|
||||
|
||||
if err = cfg.RootCommandDBSetup.Run(ctx, nil, cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logger.SetDefault(log)
|
||||
service.DefaultPermissions = p
|
||||
// if service.DefaultStore, err = store.NewWithAfero(afero.NewMemMapFs(), "test"); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
|
||||
cfg.InitServices(ctx, cfg)
|
||||
}
|
||||
|
||||
func InitApp() {
|
||||
InitConfig()
|
||||
helpers.InitAuth()
|
||||
|
||||
if r != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r = chi.NewRouter()
|
||||
r.Use(api.Base(logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
InitApp()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func newHelper(t *testing.T) helper {
|
||||
h := helper{
|
||||
t: t,
|
||||
a: require.New(t),
|
||||
roleID: factory.Sonyflake.NextID(),
|
||||
cUser: &types.User{
|
||||
ID: factory.Sonyflake.NextID(),
|
||||
},
|
||||
}
|
||||
|
||||
h.cUser.SetRoles([]uint64{h.roleID})
|
||||
|
||||
p.ClearGrants()
|
||||
|
||||
// Setup permissions with allowed access to system
|
||||
h.mockPermissions(
|
||||
permissions.AllowRule(permissions.EveryoneRoleID, types.SystemPermissionResource, "access"),
|
||||
)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// apitest basics, initialize, set handler, add auth
|
||||
func (h helper) apiInit() *apitest.APITest {
|
||||
InitApp()
|
||||
|
||||
return apitest.
|
||||
New().
|
||||
Handler(r).
|
||||
Intercept(helpers.ReqHeaderAuthBearer(h.cUser))
|
||||
}
|
||||
|
||||
func (h helper) mockPermissions(rules ...*permissions.Rule) {
|
||||
h.a.NoError(p.Grant(
|
||||
// TestService we use does not have any backend storage,
|
||||
context.Background(),
|
||||
// We want to make sure we did not make a mistake with any of the mocked resources or actions
|
||||
service.DefaultAccessControl.Whitelist(),
|
||||
rules...,
|
||||
))
|
||||
}
|
||||
|
||||
// Set allow permision for test role
|
||||
func (h helper) allow(r permissions.Resource, o permissions.Operation) {
|
||||
h.mockPermissions(permissions.AllowRule(h.roleID, r, o))
|
||||
}
|
||||
|
||||
// set deny permission for test role
|
||||
func (h helper) deny(r permissions.Resource, o permissions.Operation) {
|
||||
h.mockPermissions(permissions.DenyRule(h.roleID, r, o))
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/permissions"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsDelete(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
// Make sure our user can grant
|
||||
h.allow(types.SystemPermissionResource, "grant")
|
||||
|
||||
// New role.
|
||||
permDelRole := h.roleID + 1
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(permDelRole), 0)
|
||||
|
||||
// Setup a few fake rules for new roke
|
||||
h.mockPermissions(
|
||||
permissions.AllowRule(permDelRole, types.SystemPermissionResource, "access"),
|
||||
permissions.DenyRule(permDelRole, types.SystemPermissionResource, "application.create"),
|
||||
permissions.DenyRule(permDelRole, types.SystemPermissionResource, "user.create"),
|
||||
)
|
||||
|
||||
h.a.Len(p.FindRulesByRoleID(permDelRole), 3)
|
||||
|
||||
h.apiInit().
|
||||
Delete(fmt.Sprintf("/permissions/%d/rules", permDelRole)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
|
||||
// Make sure everything is deleted
|
||||
rr, _ := p.FindRulesByRoleID(permDelRole).Filter(func(r *permissions.Rule) (b bool, e error) {
|
||||
return r.Access != permissions.Inherit, nil
|
||||
})
|
||||
|
||||
h.a.Empty(rr)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsEffective(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.allow(types.SystemPermissionResource, "access")
|
||||
h.deny(types.SystemPermissionResource, "application.create")
|
||||
|
||||
h.apiInit().
|
||||
Get("/permissions/effective").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsList(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
|
||||
h.apiInit().
|
||||
Get("/permissions/").
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
Assert(jsonpath.Present(`$.response[? @.resource=="system"]`)).
|
||||
End()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsRead(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.allow(types.SystemPermissionResource, "access")
|
||||
h.allow(types.SystemPermissionResource, "grant")
|
||||
h.deny(types.SystemPermissionResource, "application.create")
|
||||
|
||||
h.apiInit().
|
||||
Get(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/cortezaproject/corteza-server/tests/helpers"
|
||||
)
|
||||
|
||||
func TestPermissionsUpdate(t *testing.T) {
|
||||
h := newHelper(t)
|
||||
h.allow(types.SystemPermissionResource, "grant")
|
||||
|
||||
h.apiInit().
|
||||
Patch(fmt.Sprintf("/permissions/%d/rules", h.roleID)).
|
||||
JSON(`{"rules":[{"resource":"system","operation":"application.create","access":"allow"}]}`).
|
||||
Expect(t).
|
||||
Status(http.StatusOK).
|
||||
Assert(helpers.AssertNoErrors).
|
||||
End()
|
||||
}
|
||||
Reference in New Issue
Block a user