Skip to content

Commit

Permalink
validate namespace configuration
Browse files Browse the repository at this point in the history
 * `name` must be unique on this node
 * for historical reasons, "ff_system" is a reserved string and cannot be used as a `name` or `remoteName`
 * a `database` plugin is required for every namespace
 * if `mode: multiparty` is specified, plugins _must_ include one each of `blockchain`,
  `dataexchange`, and `sharedstorage`
 * if `mode: gateway` is speicified, plugins _must not_ include `dataexchange` or `sharedstorage`
 * at most one of each type of plugin is allowed per namespace, except for tokens (which
  may have many per namespace)

Signed-off-by: Alex Shorsher <alex.shorsher@kaleido.io>
  • Loading branch information
shorsher committed May 24, 2022
1 parent 28a99d2 commit c6fd142
Show file tree
Hide file tree
Showing 9 changed files with 700 additions and 222 deletions.
2 changes: 2 additions & 0 deletions docs/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,8 @@ nav_order: 3
|---|-----------|----|-------------|
|description|A description for the namespace|`string`|`<nil>`
|name|The name of the namespace (must be unique)|`string`|`<nil>`
|plugins|The list of plugins for this namespace|`string`|`<nil>`
|remoteName|The namespace name to be sent in plugin calls, if it differs from namespace name|`string`|`<nil>`

## node

Expand Down
4 changes: 0 additions & 4 deletions internal/coreconfig/coreconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ const (
PluginConfigName = "name"
// PluginConfigType is the type of the plugin to be loaded
PluginConfigType = "type"
// NamespaceName is a short name for a pre-defined namespace
NamespaceName = "name"
// NamespaceName is a long description for a pre-defined namespace
NamespaceDescription = "description"
)

// The following keys can be access from the root configuration.
Expand Down
8 changes: 7 additions & 1 deletion internal/coreconfig/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ package coreconfig
const (
// NamespaceName is a short name for a pre-defined namespace
NamespaceName = "name"
// NamespaceName is a long description for a pre-defined namespace
// NamespaceDescription is a long description for a pre-defined namespace
NamespaceDescription = "description"
// NamespaceMode is a the configured mode of a namespace
NamespaceMode = "mode"
// NamespaceRemoteName is a the configured mode of a namespace
NamespaceRemoteName = "remoteName"
// NamespacePlugins is a the list of namespace plugins
NamespacePlugins = "plugins"
)
3 changes: 3 additions & 0 deletions internal/coremsgs/en_config_descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ var (
ConfigNamespacesDefault = ffc("config.namespaces.default", "The default namespace - must be in the predefined list", i18n.StringType)
ConfigNamespacesPredefined = ffc("config.namespaces.predefined", "A list of namespaces to ensure exists, without requiring a broadcast from the network", "List "+i18n.StringType)
ConfigNamespacesPredefinedName = ffc("config.namespaces.predefined[].name", "The name of the namespace (must be unique)", i18n.StringType)
ConfigNamespacesPredefinedPlugins = ffc("config.namespaces.predefined[].plugins", "The list of plugins for this namespace", i18n.StringType)
ConfigNamespacesPredefinedMode = ffc("config.namespaces.predefined[].mode", "The namespace mode", i18n.StringType)
ConfigNamespacesPredefinedRemoteName = ffc("config.namespaces.predefined[].remoteName", "The namespace name to be sent in plugin calls, if it differs from namespace name", i18n.StringType)
ConfigNamespacesPredefinedDescription = ffc("config.namespaces.predefined[].description", "A description for the namespace", i18n.StringType)

ConfigNodeDescription = ffc("config.node.description", "The description of this FireFly node", i18n.StringType)
Expand Down
421 changes: 214 additions & 207 deletions internal/coremsgs/en_error_messages.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions internal/namespace/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ var (
func InitConfig(withDefaults bool) {
namespacePredefined.AddKnownKey(coreconfig.NamespaceName)
namespacePredefined.AddKnownKey(coreconfig.NamespaceDescription)
namespacePredefined.AddKnownKey(coreconfig.NamespaceRemoteName)
namespacePredefined.AddKnownKey(coreconfig.NamespacePlugins)
// do we need to set the default mode?
if withDefaults {
namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default")
namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace")
namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceMode, "multiparty")
}
}
153 changes: 147 additions & 6 deletions internal/namespace/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ package namespace
import (
"context"
"fmt"
"strings"

"github.com/hyperledger/firefly-common/pkg/config"
"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly-common/pkg/log"
"github.com/hyperledger/firefly/internal/coreconfig"
"github.com/hyperledger/firefly/internal/coremsgs"
"github.com/hyperledger/firefly/pkg/blockchain"
"github.com/hyperledger/firefly/pkg/core"
"github.com/hyperledger/firefly/pkg/database"
"github.com/hyperledger/firefly/pkg/dataexchange"
"github.com/hyperledger/firefly/pkg/sharedstorage"
"github.com/hyperledger/firefly/pkg/tokens"
)

type Manager interface {
Expand All @@ -36,14 +41,24 @@ type Manager interface {
}

type namespaceManager struct {
ctx context.Context
nsConfig map[string]config.Section
ctx context.Context
nsConfig map[string]config.Section
bcPlugins map[string]blockchain.Plugin
dbPlugins map[string]database.Plugin
dxPlugins map[string]dataexchange.Plugin
ssPlugins map[string]sharedstorage.Plugin
tokensPlugins map[string]tokens.Plugin
}

func NewNamespaceManager(ctx context.Context) Manager {
func NewNamespaceManager(ctx context.Context, bc map[string]blockchain.Plugin, db map[string]database.Plugin, dx map[string]dataexchange.Plugin, ss map[string]sharedstorage.Plugin, tokens map[string]tokens.Plugin) Manager {
nm := &namespaceManager{
ctx: ctx,
nsConfig: buildNamespaceMap(ctx),
ctx: ctx,
nsConfig: buildNamespaceMap(ctx),
bcPlugins: bc,
dbPlugins: db,
dxPlugins: dx,
ssPlugins: ss,
tokensPlugins: tokens,
}
return nm
}
Expand Down Expand Up @@ -80,7 +95,7 @@ func (nm *namespaceManager) getPredefinedNamespaces(ctx context.Context) ([]*cor
i := 0
foundDefault := false
for name, nsObject := range nm.nsConfig {
if err := core.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", i)); err != nil {
if err := nm.validateNamespaceConfig(ctx, name, i, nsObject); err != nil {
return nil, err
}
i++
Expand Down Expand Up @@ -124,3 +139,129 @@ func (nm *namespaceManager) initNamespaces(ctx context.Context, di database.Plug
}
return nil
}

func (nm *namespaceManager) validateNamespaceConfig(ctx context.Context, name string, index int, conf config.Section) error {
if err := core.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", index)); err != nil {
return err
}

if strings.ToLower(name) == "ff_system" || strings.ToLower(conf.GetString(coreconfig.NamespaceRemoteName)) == "ff_system" {
return i18n.NewError(ctx, coremsgs.MsgFFSystemReservedName)
}

mode := conf.GetString(coreconfig.NamespaceMode)
plugins := conf.GetStringSlice(coreconfig.NamespacePlugins)

// If the no plugins are found when querying the config, assume older config file
if len(plugins) == 0 {
// Still check to ensure at least one bc, dx, ss, and db plugin exist
if len(nm.bcPlugins) == 0 || len(nm.dxPlugins) == 0 || len(nm.ssPlugins) == 0 || len(nm.dbPlugins) == 0 {
return i18n.NewError(ctx, coremsgs.MsgNamespaceMultipartyConfiguration, name)
}
return nil
}

switch mode {
// Multiparty is the default mode when none is provided
case "multiparty", "":
if err := nm.validateMultiPartyConfig(ctx, name, plugins); err != nil {
return err
}
case "gateway":
if err := nm.validateGatewayConfig(ctx, name, plugins); err != nil {
return err
}
default:
return i18n.NewError(ctx, coremsgs.MsgInvalidNamespaceMode, name)
}
return nil
}

func (nm *namespaceManager) validateMultiPartyConfig(ctx context.Context, name string, plugins []string) error {
var dbPlugin bool
var ssPlugin bool
var dxPlugin bool
var bcPlugin bool

for _, pluginName := range plugins {
if _, ok := nm.bcPlugins[pluginName]; ok {
if bcPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "blockchain")
}
bcPlugin = true
continue
}
if _, ok := nm.dxPlugins[pluginName]; ok {
if dxPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "dataexchange")
}
dxPlugin = true
continue
}
if _, ok := nm.ssPlugins[pluginName]; ok {
if ssPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "sharedstorage")
}
ssPlugin = true
continue
}
if _, ok := nm.dbPlugins[pluginName]; ok {
if dbPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "database")
}
dbPlugin = true
continue
}
if _, ok := nm.tokensPlugins[pluginName]; ok {
continue
}

return i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, name, pluginName)
}

if !dbPlugin || !ssPlugin || !dxPlugin || !bcPlugin {
fmt.Printf("plugins: %+v\n", nm)
return i18n.NewError(ctx, coremsgs.MsgNamespaceMultipartyConfiguration, name)
}

return nil
}

func (nm *namespaceManager) validateGatewayConfig(ctx context.Context, name string, plugins []string) error {
var dbPlugin bool
var bcPlugin bool

for _, pluginName := range plugins {
if _, ok := nm.bcPlugins[pluginName]; ok {
if bcPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "blockchain")
}
bcPlugin = true
continue
}
if _, ok := nm.dxPlugins[pluginName]; ok {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayInvalidPlugins, name)
}
if _, ok := nm.ssPlugins[pluginName]; ok {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayInvalidPlugins, name)
}
if _, ok := nm.dbPlugins[pluginName]; ok {
if dbPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayMultiplePluginType, name, "database")
}
dbPlugin = true
continue
}
if _, ok := nm.tokensPlugins[pluginName]; ok {
continue
}

return i18n.NewError(ctx, coremsgs.MsgNamespaceUnknownPlugin, name, pluginName)
}

if !dbPlugin {
return i18n.NewError(ctx, coremsgs.MsgNamespaceGatewayNoDB, name)
}

return nil
}
Loading

0 comments on commit c6fd142

Please sign in to comment.