mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Always parse config file with checks enabled
Signed-off-by: Waldemar Quevedo <wally@synadia.com>
This commit is contained in:
@@ -40,8 +40,11 @@ func TestConfigCheck(t *testing.T) {
|
||||
// errorPos is the position of the error.
|
||||
errorPos int
|
||||
|
||||
// warning errors also include a reason optionally
|
||||
// warning errors also include a reason optionally.
|
||||
reason string
|
||||
|
||||
// newDefaultErr is a configuration error that includes source of error.
|
||||
newDefaultErr error
|
||||
}{
|
||||
{
|
||||
name: "when unknown field is used at top level",
|
||||
@@ -491,6 +494,335 @@ func TestConfigCheck(t *testing.T) {
|
||||
errorPos: 5,
|
||||
reason: `setting "permissions" within cluster authorization block is deprecated`,
|
||||
},
|
||||
/////////////////////
|
||||
// ACCOUNTS //
|
||||
/////////////////////
|
||||
{
|
||||
name: "when accounts block is correctly configured",
|
||||
config: `
|
||||
http_port = 8222
|
||||
|
||||
accounts {
|
||||
|
||||
#
|
||||
# synadia > nats.io, cncf
|
||||
#
|
||||
synadia {
|
||||
# SAADJL5XAEM6BDYSWDTGVILJVY54CQXZM5ZLG4FRUAKB62HWRTPNSGXOHA
|
||||
nkey = "AC5GRL36RQV7MJ2GT6WQSCKDKJKYTK4T2LGLWJ2SEJKRDHFOQQWGGFQL"
|
||||
|
||||
users [
|
||||
{
|
||||
# SUAEL6RU3BSDAFKOHNTEOK5Q6FTM5FTAMWVIKBET6FHPO4JRII3CYELVNM
|
||||
nkey = "UCARKS2E3KVB7YORL2DG34XLT7PUCOL2SVM7YXV6ETHLW6Z46UUJ2VZ3"
|
||||
}
|
||||
]
|
||||
|
||||
exports = [
|
||||
{ service: "synadia.requests", accounts: [nats, cncf] }
|
||||
]
|
||||
}
|
||||
|
||||
#
|
||||
# nats < synadia
|
||||
#
|
||||
nats {
|
||||
# SUAJTM55JH4BNYDA22DMDZJSRBRKVDGSLYK2HDIOCM3LPWCDXIDV5Q4CIE
|
||||
nkey = "ADRZ42QBM7SXQDXXTSVWT2WLLFYOQGAFC4TO6WOAXHEKQHIXR4HFYJDS"
|
||||
|
||||
users [
|
||||
{
|
||||
# SUADZTYQAKTY5NQM7XRB5XR3C24M6ROGZLBZ6P5HJJSSOFUGC5YXOOECOM
|
||||
nkey = "UD6AYQSOIN2IN5OGC6VQZCR4H3UFMIOXSW6NNS6N53CLJA4PB56CEJJI"
|
||||
}
|
||||
]
|
||||
|
||||
imports = [
|
||||
# This account has to send requests to 'nats.requests' subject
|
||||
{ service: { account: "synadia", subject: "synadia.requests" }, to: "nats.requests" }
|
||||
]
|
||||
}
|
||||
|
||||
#
|
||||
# cncf < synadia
|
||||
#
|
||||
cncf {
|
||||
# SAAFHDZX7SGZ2SWHPS22JRPPK5WX44NPLNXQHR5C5RIF6QRI3U65VFY6C4
|
||||
nkey = "AD4YRVUJF2KASKPGRMNXTYKIYSCB3IHHB4Y2ME6B2PDIV5QJ23C2ZRIT"
|
||||
|
||||
users [
|
||||
{
|
||||
# SUAKINP3Z2BPUXWOFSW2FZC7TFJCMMU7DHKP2C62IJQUDASOCDSTDTRMJQ
|
||||
nkey = "UB57IEMPG4KOTPFV5A66QKE2HZ3XBXFHVRCCVMJEWKECMVN2HSH3VTSJ"
|
||||
}
|
||||
]
|
||||
|
||||
imports = [
|
||||
# This account has to send requests to 'synadia.requests' subject
|
||||
{ service: { account: "synadia", subject: "synadia.requests" } }
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
defaultErr: nil,
|
||||
pedanticErr: nil,
|
||||
},
|
||||
{
|
||||
name: "when accounts block has unknown fields",
|
||||
config: `
|
||||
http_port = 8222
|
||||
|
||||
accounts {
|
||||
foo = "bar"
|
||||
}
|
||||
`,
|
||||
newDefaultErr: errors.New(`Expected map entries for accounts`),
|
||||
errorLine: 4,
|
||||
errorPos: 3,
|
||||
},
|
||||
{
|
||||
name: "when accounts block defines a global account",
|
||||
config: `
|
||||
http_port = 8222
|
||||
|
||||
accounts {
|
||||
$G = {
|
||||
}
|
||||
}
|
||||
`,
|
||||
newDefaultErr: errors.New(`"$G" is a Reserved Account`),
|
||||
errorLine: 4,
|
||||
errorPos: 3,
|
||||
},
|
||||
{
|
||||
name: "when accounts block uses an invalid public key",
|
||||
config: `
|
||||
accounts {
|
||||
synadia = {
|
||||
nkey = "invalid"
|
||||
}
|
||||
}
|
||||
`,
|
||||
newDefaultErr: errors.New(`Not a valid public nkey for an account: "invalid"`),
|
||||
errorLine: 4,
|
||||
errorPos: 21,
|
||||
},
|
||||
{
|
||||
name: "when accounts list includes reserved account",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = [foo, bar, "$G"]
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`"$G" is a Reserved Account`),
|
||||
errorLine: 4,
|
||||
errorPos: 3,
|
||||
},
|
||||
{
|
||||
name: "when accounts list includes a dupe entry",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = [foo, bar, bar]
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Duplicate Account Entry: bar`),
|
||||
errorLine: 4,
|
||||
errorPos: 3,
|
||||
},
|
||||
{
|
||||
name: "when accounts block includes a dupe user",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
users = [
|
||||
{ user: "foo", pass: "bar" },
|
||||
{ user: "hello", pass: "world" },
|
||||
{ user: "foo", pass: "bar" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Duplicate user "foo" detected`),
|
||||
errorLine: 6,
|
||||
errorPos: 21,
|
||||
},
|
||||
{
|
||||
name: "when accounts block includes a dupe nkey",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
users = [
|
||||
{ nkey = "UB57IEMPG4KOTPFV5A66QKE2HZ3XBXFHVRCCVMJEWKECMVN2HSH3VTSJ" },
|
||||
{ nkey = "UB57IEMPG4KOTPFV5A66QKE2HZ3XBXFHVRCCVMJEWKECMVN2HSH3VTSJ" },
|
||||
{ nkey = "UB57IEMPG4KOTPFV5A66QKE2HZ3XBXFHVRCCVMJEWKECMVN2HSH3VTSJ" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Duplicate nkey "UB57IEMPG4KOTPFV5A66QKE2HZ3XBXFHVRCCVMJEWKECMVN2HSH3VTSJ" detected`),
|
||||
errorLine: 6,
|
||||
errorPos: 21,
|
||||
},
|
||||
{
|
||||
name: "when accounts block imports are not a list",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
imports = true
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Imports should be an array, got bool`),
|
||||
errorLine: 6,
|
||||
errorPos: 21,
|
||||
},
|
||||
{
|
||||
name: "when accounts block exports are not a list",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
exports = true
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Exports should be an array, got bool`),
|
||||
errorLine: 6,
|
||||
errorPos: 21,
|
||||
},
|
||||
{
|
||||
name: "when accounts block imports items are not a map",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
imports = [
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Import Items should be a map with type entry, got bool`),
|
||||
errorLine: 7,
|
||||
errorPos: 23,
|
||||
},
|
||||
{
|
||||
name: "when accounts block export items are not a map",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
exports = [
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Export Items should be a map with type entry, got bool`),
|
||||
errorLine: 7,
|
||||
errorPos: 23,
|
||||
},
|
||||
{
|
||||
name: "when accounts exports has a stream name that is not a string",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
exports = [
|
||||
{
|
||||
stream: false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Expected stream name to be string, got bool`),
|
||||
errorLine: 8,
|
||||
errorPos: 25,
|
||||
},
|
||||
{
|
||||
name: "when accounts exports has a service name that is not a string",
|
||||
config: `
|
||||
accounts = {
|
||||
nats {
|
||||
exports = [
|
||||
{
|
||||
service: false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
newDefaultErr: errors.New(`Expected service name to be string, got bool`),
|
||||
errorLine: 6,
|
||||
errorPos: 25,
|
||||
},
|
||||
{
|
||||
name: "when accounts imports stream without name",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
imports = [
|
||||
{ stream: { }}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Expect an account name and a subject`),
|
||||
errorLine: 7,
|
||||
errorPos: 25,
|
||||
},
|
||||
{
|
||||
name: "when accounts imports service without name",
|
||||
config: `
|
||||
port = 4222
|
||||
|
||||
accounts = {
|
||||
nats {
|
||||
imports = [
|
||||
{ service: { }}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
http_port = 8222
|
||||
`,
|
||||
newDefaultErr: errors.New(`Expect an account name and a subject`),
|
||||
errorLine: 7,
|
||||
errorPos: 25,
|
||||
},
|
||||
}
|
||||
|
||||
checkConfig := func(config string, pedantic bool) error {
|
||||
@@ -518,7 +850,16 @@ func TestConfigCheck(t *testing.T) {
|
||||
|
||||
t.Run("with pedantic check enabled", func(t *testing.T) {
|
||||
err := checkConfig(conf, true)
|
||||
expectedErr := test.pedanticErr
|
||||
var expectedErr error
|
||||
|
||||
// New default errors also include source of error
|
||||
// like an error reported when running with pedantic flag.
|
||||
if test.newDefaultErr != nil {
|
||||
expectedErr = test.newDefaultErr
|
||||
} else if test.pedanticErr != nil {
|
||||
expectedErr = test.pedanticErr
|
||||
}
|
||||
|
||||
if err != nil && expectedErr != nil {
|
||||
msg := fmt.Sprintf("%s:%d:%d: %s", conf, test.errorLine, test.errorPos, expectedErr.Error())
|
||||
if test.reason != "" {
|
||||
@@ -528,16 +869,28 @@ func TestConfigCheck(t *testing.T) {
|
||||
t.Errorf("Expected %q, got %q", msg, err.Error())
|
||||
}
|
||||
}
|
||||
checkErr(t, err, test.pedanticErr)
|
||||
|
||||
checkErr(t, err, expectedErr)
|
||||
})
|
||||
|
||||
t.Run("with pedantic check disabled", func(t *testing.T) {
|
||||
err := checkConfig(conf, false)
|
||||
expectedErr := test.defaultErr
|
||||
if err != nil && expectedErr != nil && err.Error() != expectedErr.Error() {
|
||||
t.Errorf("Expected %q, got %q", expectedErr.Error(), err.Error())
|
||||
|
||||
// Gradually move to all errors including source of the error.
|
||||
if err != nil && test.newDefaultErr != nil {
|
||||
expectedErr := test.newDefaultErr
|
||||
source := fmt.Sprintf("%s:%d:%d", conf, test.errorLine, test.errorPos)
|
||||
expectedMsg := fmt.Sprintf("%s: %s", source, expectedErr.Error())
|
||||
if err.Error() != expectedMsg {
|
||||
t.Errorf("\nExpected: \n%q, \ngot: \n%q", expectedMsg, err.Error())
|
||||
}
|
||||
} else if err != nil && test.defaultErr != nil {
|
||||
expectedErr := test.defaultErr
|
||||
if err != nil && expectedErr != nil && err.Error() != expectedErr.Error() {
|
||||
t.Errorf("Expected: \n%q, \ngot: \n%q", expectedErr.Error(), err.Error())
|
||||
}
|
||||
checkErr(t, err, test.defaultErr)
|
||||
}
|
||||
checkErr(t, err, test.defaultErr)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -562,6 +915,6 @@ func TestConfigCheckIncludes(t *testing.T) {
|
||||
}
|
||||
expectedErr := errors.New(`configs/include_bad_conf_check_b.conf:10:19: unknown field "monitoring_port"`)
|
||||
if err != nil && expectedErr != nil && err.Error() != expectedErr.Error() {
|
||||
t.Errorf("Expected %q, got %q", expectedErr.Error(), err.Error())
|
||||
t.Errorf("Expected: \n%q, got\n: %q", expectedErr.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
322
server/opts.go
322
server/opts.go
@@ -199,34 +199,41 @@ type token interface {
|
||||
Position() int
|
||||
}
|
||||
|
||||
// configErr is a configuration error.
|
||||
type configErr struct {
|
||||
token token
|
||||
reason string
|
||||
}
|
||||
|
||||
func (e *configErr) Source() string {
|
||||
return fmt.Sprintf("%s:%d:%d", e.token.SourceFile(), e.token.Line(), e.token.Position())
|
||||
}
|
||||
|
||||
func (e *configErr) Error() string {
|
||||
if e.token != nil {
|
||||
return fmt.Sprintf("%s: %s", e.Source(), e.reason)
|
||||
}
|
||||
return e.reason
|
||||
}
|
||||
|
||||
// unknownConfigFieldErr is an error reported in pedantic mode.
|
||||
type unknownConfigFieldErr struct {
|
||||
field string
|
||||
token token
|
||||
configFile string
|
||||
configErr
|
||||
field string
|
||||
}
|
||||
|
||||
func (e *unknownConfigFieldErr) Error() string {
|
||||
msg := fmt.Sprintf("unknown field %q", e.field)
|
||||
if e.token != nil {
|
||||
return fmt.Sprintf("%s:%d:%d: %s", e.configFile, e.token.Line(), e.token.Position(), msg)
|
||||
}
|
||||
return msg
|
||||
return fmt.Sprintf("%s: unknown field %q", e.Source(), e.field)
|
||||
}
|
||||
|
||||
// configWarningErr is an error reported in pedantic mode.
|
||||
type configWarningErr struct {
|
||||
field string
|
||||
token token
|
||||
configFile string
|
||||
reason string
|
||||
configErr
|
||||
field string
|
||||
}
|
||||
|
||||
func (e *configWarningErr) Error() string {
|
||||
msg := fmt.Sprintf("invalid use of field %q", e.field)
|
||||
if e.token != nil {
|
||||
msg = fmt.Sprintf("%s:%d:%d: %s", e.configFile, e.token.Line(), e.token.Position(), msg)
|
||||
}
|
||||
msg += ": " + e.reason
|
||||
return msg
|
||||
return fmt.Sprintf("%s: invalid use of field %q: %s", e.Source(), e.field, e.reason)
|
||||
}
|
||||
|
||||
// ProcessConfigFile processes a configuration file.
|
||||
@@ -270,25 +277,14 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
if configFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
m map[string]interface{}
|
||||
tk token
|
||||
pedantic bool = o.CheckConfig
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
m, err = conf.ParseFileWithChecks(configFile)
|
||||
} else {
|
||||
m, err = conf.ParseFile(configFile)
|
||||
}
|
||||
m, err := conf.ParseFileWithChecks(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
// When pedantic checks are enabled then need to unwrap
|
||||
// to get the value along with reported error line.
|
||||
tk, v = unwrapValue(v)
|
||||
tk, v := unwrapValue(v)
|
||||
switch strings.ToLower(k) {
|
||||
case "listen":
|
||||
hp, err := parseListen(v)
|
||||
@@ -310,17 +306,12 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
case "logtime":
|
||||
o.Logtime = v.(bool)
|
||||
case "accounts":
|
||||
err = parseAccounts(v, o)
|
||||
err := parseAccounts(tk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "authorization":
|
||||
var auth *authorization
|
||||
if pedantic {
|
||||
auth, err = parseAuthorization(tk, o)
|
||||
} else {
|
||||
auth, err = parseAuthorization(v, o)
|
||||
}
|
||||
auth, err := parseAuthorization(tk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -366,12 +357,7 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
case "https_port":
|
||||
o.HTTPSPort = int(v.(int64))
|
||||
case "cluster":
|
||||
var err error
|
||||
if pedantic {
|
||||
err = parseCluster(tk, o)
|
||||
} else {
|
||||
err = parseCluster(v, o)
|
||||
}
|
||||
err := parseCluster(tk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -406,11 +392,7 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
tc *TLSConfigOpts
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
tc, err = parseTLS(tk, o)
|
||||
} else {
|
||||
tc, err = parseTLS(v, o)
|
||||
}
|
||||
tc, err = parseTLS(tk, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -433,11 +415,13 @@ func (o *Options) ProcessConfigFile(configFile string) error {
|
||||
fmt.Printf("WARNING: write_deadline should be converted to a duration\n")
|
||||
}
|
||||
default:
|
||||
pedantic := o.CheckConfig
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: k,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -497,15 +481,7 @@ func parseCluster(v interface{}, opts *Options) error {
|
||||
case "host", "net":
|
||||
opts.Cluster.Host = mv.(string)
|
||||
case "authorization":
|
||||
var (
|
||||
auth *authorization
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
auth, err = parseAuthorization(tk, opts)
|
||||
} else {
|
||||
auth, err = parseAuthorization(mv, opts)
|
||||
}
|
||||
auth, err := parseAuthorization(tk, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -519,10 +495,11 @@ func parseCluster(v interface{}, opts *Options) error {
|
||||
if auth.defaultPermissions != nil {
|
||||
if pedantic {
|
||||
return &configWarningErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
reason: `setting "permissions" within cluster authorization block is deprecated`,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
reason: `setting "permissions" within cluster authorization block is deprecated`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,11 +525,7 @@ func parseCluster(v interface{}, opts *Options) error {
|
||||
tc *TLSConfigOpts
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
tc, err = parseTLS(tk, opts)
|
||||
} else {
|
||||
tc, err = parseTLS(mv, opts)
|
||||
}
|
||||
tc, err = parseTLS(tk, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -581,9 +554,10 @@ func parseCluster(v interface{}, opts *Options) error {
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,19 +615,20 @@ func parseAccounts(v interface{}, opts *Options) error {
|
||||
exportStreams []*export
|
||||
exportServices []*export
|
||||
)
|
||||
_, v = unwrapValue(v)
|
||||
switch v.(type) {
|
||||
tk, v := unwrapValue(v)
|
||||
switch vv := v.(type) {
|
||||
// Simple array of account names.
|
||||
case []interface{}, []string:
|
||||
m := make(map[string]struct{}, len(v.([]interface{})))
|
||||
for _, name := range v.([]interface{}) {
|
||||
for _, n := range v.([]interface{}) {
|
||||
_, name := unwrapValue(n)
|
||||
ns := name.(string)
|
||||
// Check for reserved names.
|
||||
if isReservedAccount(ns) {
|
||||
return fmt.Errorf("%q is a Reserved Account", ns)
|
||||
return &configErr{tk, fmt.Sprintf("%q is a Reserved Account", ns)}
|
||||
}
|
||||
if _, ok := m[ns]; ok {
|
||||
return fmt.Errorf("Duplicate Account Entry: %s", ns)
|
||||
return &configErr{tk, fmt.Sprintf("Duplicate Account Entry: %s", ns)}
|
||||
}
|
||||
opts.Accounts = append(opts.Accounts, &Account{Name: ns})
|
||||
m[ns] = struct{}{}
|
||||
@@ -663,15 +638,15 @@ func parseAccounts(v interface{}, opts *Options) error {
|
||||
// Track users across accounts, must be unique across
|
||||
// accounts and nkeys vs users.
|
||||
uorn := make(map[string]struct{})
|
||||
for aname, mv := range v.(map[string]interface{}) {
|
||||
for aname, mv := range vv {
|
||||
_, amv := unwrapValue(mv)
|
||||
// These should be maps.
|
||||
mv, ok := amv.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Expected map entries for accounts")
|
||||
return &configErr{tk, "Expected map entries for accounts"}
|
||||
}
|
||||
if isReservedAccount(aname) {
|
||||
return fmt.Errorf("%q is a Reserved Account", aname)
|
||||
return &configErr{tk, fmt.Sprintf("%q is a Reserved Account", aname)}
|
||||
}
|
||||
acc := &Account{Name: aname}
|
||||
opts.Accounts = append(opts.Accounts, acc)
|
||||
@@ -682,40 +657,31 @@ func parseAccounts(v interface{}, opts *Options) error {
|
||||
case "nkey":
|
||||
nk, ok := mv.(string)
|
||||
if !ok || !nkeys.IsValidPublicAccountKey(nk) {
|
||||
return fmt.Errorf("Not a valid public nkey for an account: %q", v)
|
||||
return &configErr{tk, fmt.Sprintf("Not a valid public nkey for an account: %q", mv)}
|
||||
}
|
||||
acc.Nkey = nk
|
||||
case "imports":
|
||||
streams, services, err := parseAccountImports(mv, acc, pedantic)
|
||||
streams, services, err := parseAccountImports(tk, acc, pedantic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
importStreams = append(importStreams, streams...)
|
||||
importServices = append(importServices, services...)
|
||||
case "exports":
|
||||
streams, services, err := parseAccountExports(mv, acc, pedantic)
|
||||
streams, services, err := parseAccountExports(tk, acc, pedantic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exportStreams = append(exportStreams, streams...)
|
||||
exportServices = append(exportServices, services...)
|
||||
case "users":
|
||||
var (
|
||||
users []*User
|
||||
err error
|
||||
nkeys []*NkeyUser
|
||||
)
|
||||
if pedantic {
|
||||
nkeys, users, err = parseUsers(tk, opts)
|
||||
} else {
|
||||
nkeys, users, err = parseUsers(mv, opts)
|
||||
}
|
||||
nkeys, users, err := parseUsers(mv, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, u := range users {
|
||||
if _, ok := uorn[u.Username]; ok {
|
||||
return fmt.Errorf("Duplicate user %q detected", u.Username)
|
||||
return &configErr{tk, fmt.Sprintf("Duplicate user %q detected", u.Username)}
|
||||
}
|
||||
uorn[u.Username] = struct{}{}
|
||||
u.Account = acc
|
||||
@@ -724,18 +690,19 @@ func parseAccounts(v interface{}, opts *Options) error {
|
||||
|
||||
for _, u := range nkeys {
|
||||
if _, ok := uorn[u.Nkey]; ok {
|
||||
return fmt.Errorf("Duplicate nkey %q detected", u.Nkey)
|
||||
return &configErr{tk, fmt.Sprintf("Duplicate nkey %q detected", u.Nkey)}
|
||||
}
|
||||
uorn[u.Nkey] = struct{}{}
|
||||
u.Account = acc
|
||||
}
|
||||
opts.Nkeys = append(opts.Nkeys, nkeys...)
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
if pedantic && !tk.IsUsedVariable() {
|
||||
return &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: k,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,23 +776,18 @@ func parseAccounts(v interface{}, opts *Options) error {
|
||||
// Parse the account imports
|
||||
func parseAccountExports(v interface{}, acc *Account, pedantic bool) ([]*export, []*export, error) {
|
||||
// This should be an array of objects/maps.
|
||||
_, v = unwrapValue(v)
|
||||
tk, v := unwrapValue(v)
|
||||
ims, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Exports should be an array, got %T", v)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Exports should be an array, got %T", v)}
|
||||
}
|
||||
|
||||
var services []*export
|
||||
var streams []*export
|
||||
|
||||
for _, v := range ims {
|
||||
_, mv := unwrapValue(v)
|
||||
io, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Export Items should be a map with type entry, got %T", mv)
|
||||
}
|
||||
// Should have stream or service
|
||||
stream, service, err := parseExportStreamOrService(io, pedantic)
|
||||
stream, service, err := parseExportStreamOrService(v, pedantic)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -844,23 +806,18 @@ func parseAccountExports(v interface{}, acc *Account, pedantic bool) ([]*export,
|
||||
// Parse the account imports
|
||||
func parseAccountImports(v interface{}, acc *Account, pedantic bool) ([]*importStream, []*importService, error) {
|
||||
// This should be an array of objects/maps.
|
||||
_, v = unwrapValue(v)
|
||||
tk, v := unwrapValue(v)
|
||||
ims, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Imports should be an array, got %T", v)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Imports should be an array, got %T", v)}
|
||||
}
|
||||
|
||||
var services []*importService
|
||||
var streams []*importStream
|
||||
|
||||
for _, v := range ims {
|
||||
_, mv := unwrapValue(v)
|
||||
io, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Import Items should be a map with type entry, got %T", mv)
|
||||
}
|
||||
// Should have stream or service
|
||||
stream, service, err := parseImportStreamOrService(io, pedantic)
|
||||
stream, service, err := parseImportStreamOrService(v, pedantic)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -889,16 +846,14 @@ func parseAccount(v map[string]interface{}, pedantic bool) (string, string, erro
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return "", "", &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if accountName == "" || subject == "" {
|
||||
return "", "", fmt.Errorf("Expect an account name and a subject")
|
||||
}
|
||||
return accountName, subject, nil
|
||||
}
|
||||
|
||||
@@ -908,31 +863,40 @@ func parseAccount(v map[string]interface{}, pedantic bool) (string, string, erro
|
||||
// {stream: "synadia.private.>", accounts: [cncf, natsio]}
|
||||
// {service: "pub.request"} # No accounts means public.
|
||||
// {service: "pub.special.request", accounts: [nats.io]}
|
||||
func parseExportStreamOrService(v map[string]interface{}, pedantic bool) (*export, *export, error) {
|
||||
func parseExportStreamOrService(v interface{}, pedantic bool) (*export, *export, error) {
|
||||
var (
|
||||
curStream *export
|
||||
curService *export
|
||||
accounts []string
|
||||
)
|
||||
|
||||
for mk, mv := range v {
|
||||
tk, v := unwrapValue(v)
|
||||
vv, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Export Items should be a map with type entry, got %T", v)}
|
||||
}
|
||||
for mk, mv := range vv {
|
||||
tk, mv := unwrapValue(mv)
|
||||
switch strings.ToLower(mk) {
|
||||
case "stream":
|
||||
if curService != nil {
|
||||
return nil, nil, fmt.Errorf("Detected stream but already saw a service: %+v", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Detected stream %q but already saw a service", mv)}
|
||||
}
|
||||
curStream = &export{sub: mv.(string)}
|
||||
|
||||
mvs, ok := mv.(string)
|
||||
if !ok {
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Expected stream name to be string, got %T", mv)}
|
||||
}
|
||||
curStream = &export{sub: mvs}
|
||||
if accounts != nil {
|
||||
curStream.accs = accounts
|
||||
}
|
||||
case "service":
|
||||
if curStream != nil {
|
||||
return nil, nil, fmt.Errorf("Detected service but already saw a stream: %+v", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Detected service %q but already saw a stream", mv)}
|
||||
}
|
||||
mvs, ok := mv.(string)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Expected service to be string name, got %T", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Expected service name to be string, got %T", mv)}
|
||||
}
|
||||
curService = &export{sub: mvs}
|
||||
if accounts != nil {
|
||||
@@ -951,9 +915,10 @@ func parseExportStreamOrService(v map[string]interface{}, pedantic bool) (*expor
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, nil, fmt.Errorf("Unknown field %q parsing export service or stream", mk)
|
||||
@@ -968,46 +933,56 @@ func parseExportStreamOrService(v map[string]interface{}, pedantic bool) (*expor
|
||||
// {stream: {account: "synadia", subject:"public.synadia"}, prefix: "imports.synadia"}
|
||||
// {stream: {account: "synadia", subject:"synadia.private.*"}}
|
||||
// {service: {account: "synadia", subject: "pub.special.request"}, subject: "synadia.request"}
|
||||
func parseImportStreamOrService(v map[string]interface{}, pedantic bool) (*importStream, *importService, error) {
|
||||
func parseImportStreamOrService(v interface{}, pedantic bool) (*importStream, *importService, error) {
|
||||
var (
|
||||
curStream *importStream
|
||||
curService *importService
|
||||
pre, to string
|
||||
)
|
||||
|
||||
for mk, mv := range v {
|
||||
tk, mv := unwrapValue(v)
|
||||
vv, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Import Items should be a map with type entry, got %T", mv)}
|
||||
}
|
||||
for mk, mv := range vv {
|
||||
tk, mv := unwrapValue(mv)
|
||||
switch strings.ToLower(mk) {
|
||||
case "stream":
|
||||
if curService != nil {
|
||||
return nil, nil, fmt.Errorf("Detected stream but already saw a service: %+v", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Detected stream but already saw a service")}
|
||||
}
|
||||
ac, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Stream entry should be an account map, got %T", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Stream entry should be an account map, got %T", mv)}
|
||||
}
|
||||
// Make sure this is a map with account and subject
|
||||
accountName, subject, err := parseAccount(ac, pedantic)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if accountName == "" || subject == "" {
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Expect an account name and a subject")}
|
||||
}
|
||||
curStream = &importStream{an: accountName, sub: subject}
|
||||
if pre != "" {
|
||||
curStream.pre = pre
|
||||
}
|
||||
case "service":
|
||||
if curStream != nil {
|
||||
return nil, nil, fmt.Errorf("Detected service but already saw a stream: %+v", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Detected service but already saw a stream")}
|
||||
}
|
||||
ac, ok := mv.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Service entry should be an account map, got %T", mv)
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Service entry should be an account map, got %T", mv)}
|
||||
}
|
||||
// Make sure this is a map with account and subject
|
||||
accountName, subject, err := parseAccount(ac, pedantic)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if accountName == "" || subject == "" {
|
||||
return nil, nil, &configErr{tk, fmt.Sprintf("Expect an account name and a subject")}
|
||||
}
|
||||
curService = &importService{an: accountName, sub: subject}
|
||||
if to != "" {
|
||||
curService.to = to
|
||||
@@ -1025,9 +1000,10 @@ func parseImportStreamOrService(v map[string]interface{}, pedantic bool) (*impor
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, nil, fmt.Errorf("Unknown field %q parsing import service or stream", mk)
|
||||
@@ -1068,31 +1044,14 @@ func parseAuthorization(v interface{}, opts *Options) (*authorization, error) {
|
||||
}
|
||||
auth.timeout = at
|
||||
case "users":
|
||||
var (
|
||||
users []*User
|
||||
err error
|
||||
nkeys []*NkeyUser
|
||||
)
|
||||
if pedantic {
|
||||
nkeys, users, err = parseUsers(tk, opts)
|
||||
} else {
|
||||
nkeys, users, err = parseUsers(mv, opts)
|
||||
}
|
||||
nkeys, users, err := parseUsers(tk, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth.users = users
|
||||
auth.nkeys = nkeys
|
||||
case "default_permission", "default_permissions", "permissions":
|
||||
var (
|
||||
permissions *Permissions
|
||||
err error
|
||||
)
|
||||
if pedantic {
|
||||
permissions, err = parseUserPermissions(tk, opts)
|
||||
} else {
|
||||
permissions, err = parseUserPermissions(mv, opts)
|
||||
}
|
||||
permissions, err := parseUserPermissions(tk, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1100,9 +1059,10 @@ func parseAuthorization(v interface{}, opts *Options) (*authorization, error) {
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1162,20 +1122,17 @@ func parseUsers(mv interface{}, opts *Options) ([]*NkeyUser, []*User, error) {
|
||||
case "pass", "password":
|
||||
user.Password = v.(string)
|
||||
case "permission", "permissions", "authorization":
|
||||
if pedantic {
|
||||
perms, err = parseUserPermissions(tk, opts)
|
||||
} else {
|
||||
perms, err = parseUserPermissions(v, opts)
|
||||
}
|
||||
perms, err = parseUserPermissions(v, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: k,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1246,9 +1203,10 @@ func parseUserPermissions(mv interface{}, opts *Options) (*Permissions, error) {
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: k,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown field %s parsing permissions", k)
|
||||
@@ -1331,9 +1289,10 @@ func parseSubjectPermission(v interface{}, opts *Options) (*SubjectPermission, e
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: k,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: k,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown field name %q parsing subject permissions, only 'allow' or 'deny' are permitted", k)
|
||||
@@ -1459,9 +1418,10 @@ func parseTLS(v interface{}, opts *Options) (*TLSConfigOpts, error) {
|
||||
default:
|
||||
if pedantic && tk != nil && !tk.IsUsedVariable() {
|
||||
return nil, &unknownConfigFieldErr{
|
||||
field: mk,
|
||||
token: tk,
|
||||
configFile: tk.SourceFile(),
|
||||
field: mk,
|
||||
configErr: configErr{
|
||||
token: tk,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user