snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
341
server/federation/service/exposed_module.go
Normal file
341
server/federation/service/exposed_module.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
|
||||
cs "github.com/cortezaproject/corteza/server/compose/service"
|
||||
ct "github.com/cortezaproject/corteza/server/compose/types"
|
||||
"github.com/cortezaproject/corteza/server/federation/types"
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/rbac"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
ss "github.com/cortezaproject/corteza/server/system/service"
|
||||
st "github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
exposedModule struct {
|
||||
node node
|
||||
ac exposedModuleAccessController
|
||||
module cs.ModuleService
|
||||
namespace cs.NamespaceService
|
||||
role ss.RoleService
|
||||
store store.Storer
|
||||
actionlog actionlog.Recorder
|
||||
}
|
||||
|
||||
exposedModuleAccessController interface {
|
||||
CanCreateModuleOnNode(ctx context.Context, r *types.Node) bool
|
||||
CanManageExposedModule(ctx context.Context, r *types.ExposedModule) bool
|
||||
}
|
||||
|
||||
ExposedModuleService interface {
|
||||
Create(ctx context.Context, new *types.ExposedModule) (*types.ExposedModule, error)
|
||||
Update(ctx context.Context, updated *types.ExposedModule) (*types.ExposedModule, error)
|
||||
Find(ctx context.Context, filter types.ExposedModuleFilter) (types.ExposedModuleSet, types.ExposedModuleFilter, error)
|
||||
FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (*types.ExposedModule, error)
|
||||
DeleteByID(ctx context.Context, nodeID, moduleID uint64) (*types.ExposedModule, error)
|
||||
}
|
||||
|
||||
moduleUpdateHandler func(ctx context.Context, ns *types.Node, c *types.ExposedModule) (bool, bool, error)
|
||||
)
|
||||
|
||||
func ExposedModule() *exposedModule {
|
||||
return &exposedModule{
|
||||
ac: DefaultAccessControl,
|
||||
node: *DefaultNode,
|
||||
module: cs.DefaultModule,
|
||||
role: ss.DefaultRole,
|
||||
namespace: cs.DefaultNamespace,
|
||||
store: DefaultStore,
|
||||
actionlog: DefaultActionlog,
|
||||
}
|
||||
}
|
||||
|
||||
// FindByAny tries to find module in a particular namespace by id, handle or name
|
||||
func (svc exposedModule) FindByAny(ctx context.Context, nodeID uint64, identifier interface{}) (m *types.ExposedModule, err error) {
|
||||
if ID, ok := identifier.(uint64); ok {
|
||||
m, err = svc.FindByID(ctx, nodeID, ID)
|
||||
} else if strIdentifier, ok := identifier.(string); ok {
|
||||
if ID, _ := strconv.ParseUint(strIdentifier, 10, 64); ID > 0 {
|
||||
m, err = svc.FindByID(ctx, nodeID, ID)
|
||||
}
|
||||
} else {
|
||||
// force invalid ID error
|
||||
// we do that to wrap error with lookup action context
|
||||
_, err = svc.FindByID(ctx, nodeID, 0)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (svc exposedModule) FindByID(ctx context.Context, nodeID uint64, moduleID uint64) (module *types.ExposedModule, err error) {
|
||||
err = func() error {
|
||||
if module, err = loadExposedModule(ctx, svc.store, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, module) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return module, err
|
||||
}
|
||||
|
||||
func (svc exposedModule) Update(ctx context.Context, updated *types.ExposedModule) (*types.ExposedModule, error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{update: updated}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
node *types.Node
|
||||
old *types.ExposedModule
|
||||
)
|
||||
|
||||
if node, err = svc.node.FindByID(ctx, updated.NodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, updated) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, updated.ComposeNamespaceID); err != nil {
|
||||
return ExposedModuleErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, updated.ComposeNamespaceID, updated.ComposeModuleID); err != nil {
|
||||
return ExposedModuleErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if old, err = svc.FindByID(ctx, updated.NodeID, updated.ID); err != nil {
|
||||
return ExposedModuleErrNotFound()
|
||||
}
|
||||
|
||||
updated.UpdatedAt = now()
|
||||
updated.CreatedAt = old.CreatedAt
|
||||
updated.UpdatedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", node.BaseURL)
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.setModule(updated)
|
||||
|
||||
if err = store.UpdateFederationExposedModule(ctx, s, updated); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return updated, svc.recordAction(ctx, aProps, ExposedModuleActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) updater(ctx context.Context, nodeID, moduleID uint64, action func(...*exposedModuleActionProps) *exposedModuleAction, fn moduleUpdateHandler) (*types.ExposedModule, error) {
|
||||
var (
|
||||
moduleChanged, fieldsChanged bool
|
||||
|
||||
n *types.Node
|
||||
m *types.ExposedModule
|
||||
aProps = &exposedModuleActionProps{module: &types.ExposedModule{ID: moduleID, NodeID: nodeID}}
|
||||
err error
|
||||
)
|
||||
|
||||
err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
if m, err = loadExposedModule(ctx, svc.store, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO - handle node id also
|
||||
if moduleChanged, fieldsChanged, err = fn(ctx, n, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = moduleChanged
|
||||
_ = fieldsChanged
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return m, svc.recordAction(ctx, aProps, action, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) DeleteByID(ctx context.Context, nodeID, moduleID uint64) (m *types.ExposedModule, err error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{}
|
||||
)
|
||||
|
||||
err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *types.ExposedModule
|
||||
)
|
||||
|
||||
if _, err = svc.node.FindByID(ctx, nodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.FindByID(ctx, nodeID, moduleID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svc.ac.CanManageExposedModule(ctx, m) {
|
||||
return ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
m.DeletedAt = now()
|
||||
m.DeletedBy = auth.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
if err = store.UpdateFederationExposedModule(ctx, s, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.delete = m
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return m, svc.recordAction(ctx, aProps, ExposedModuleActionDelete, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) Find(ctx context.Context, filter types.ExposedModuleFilter) (set types.ExposedModuleSet, f types.ExposedModuleFilter, err error) {
|
||||
filter.Check = func(res *types.ExposedModule) (bool, error) {
|
||||
if !svc.ac.CanManageExposedModule(ctx, res) {
|
||||
return false, ExposedModuleErrNotAllowedToManage()
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
if set, f, err = store.SearchFederationExposedModules(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
return set, f, err
|
||||
}
|
||||
|
||||
func (svc exposedModule) Create(ctx context.Context, new *types.ExposedModule) (*types.ExposedModule, error) {
|
||||
var (
|
||||
aProps = &exposedModuleActionProps{create: new}
|
||||
)
|
||||
|
||||
err := store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) (err error) {
|
||||
var (
|
||||
m *ct.Module
|
||||
node *types.Node
|
||||
fedRole *st.Role
|
||||
)
|
||||
|
||||
if node, err = svc.node.FindByID(ctx, new.NodeID); err != nil {
|
||||
return ExposedModuleErrNodeNotFound()
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateModuleOnNode(ctx, node) {
|
||||
return ExposedModuleErrNotAllowedToCreate()
|
||||
}
|
||||
|
||||
if _, err := svc.namespace.FindByID(ctx, new.ComposeNamespaceID); err != nil {
|
||||
return ExposedModuleErrComposeNamespaceNotFound()
|
||||
}
|
||||
|
||||
if m, err = svc.module.FindByID(ctx, new.ComposeNamespaceID, new.ComposeModuleID); err != nil {
|
||||
return ExposedModuleErrComposeModuleNotFound()
|
||||
}
|
||||
|
||||
if err = svc.uniqueCheck(ctx, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctxIdentity := auth.GetIdentityFromContext(ctx)
|
||||
|
||||
new.ID = nextID()
|
||||
new.CreatedAt = *now()
|
||||
new.CreatedBy = ctxIdentity.Identity()
|
||||
|
||||
// find the federation role
|
||||
if fedRole, err = svc.role.FindByHandle(ctx, "federation"); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fedRole != nil {
|
||||
// get first ID from role and add it as allow rule
|
||||
err = cs.DefaultAccessControl.Grant(ctx, rbac.AllowRule(fedRole.ID, ct.RecordRbacResource(m.NamespaceID, m.ID, 0), "read"))
|
||||
}
|
||||
|
||||
// set labels
|
||||
AddFederationLabel(m, "federation", node.BaseURL)
|
||||
|
||||
if _, err := svc.module.Update(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aProps.setModule(new)
|
||||
|
||||
if err = store.CreateFederationExposedModule(ctx, s, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return new, svc.recordAction(ctx, aProps, ExposedModuleActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc exposedModule) uniqueCheck(ctx context.Context, m *types.ExposedModule) (err error) {
|
||||
f := types.ExposedModuleFilter{
|
||||
NodeID: m.NodeID,
|
||||
ComposeModuleID: m.ComposeModuleID,
|
||||
ComposeNamespaceID: m.ComposeNamespaceID,
|
||||
}
|
||||
|
||||
set, _, err := store.SearchFederationExposedModules(ctx, svc.store, f)
|
||||
|
||||
if len(set) > 0 && err == nil {
|
||||
return ExposedModuleErrNotUnique()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadExposedModule(ctx context.Context, s store.FederationExposedModules, nodeID, ID uint64) (res *types.ExposedModule, err error) {
|
||||
if ID == 0 || nodeID == 0 {
|
||||
return nil, SharedModuleErrInvalidID()
|
||||
}
|
||||
|
||||
if res, err = store.LookupFederationExposedModuleByID(ctx, s, ID); errors.IsNotFound(err) {
|
||||
err = SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if nodeID != res.NodeID {
|
||||
// Make sure chart belongs to the right namespace
|
||||
return nil, SharedModuleErrNotFound()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user