mirror of https://github.com/hashicorp/consul
Handle rules translation when coming from the JSON compat HCL (#5662)
We were not handling some object keys when they were strings instead of identifiers. Now both are handled. Fixes #5493pull/5665/head
parent
c6be3b525b
commit
f88d1ccc36
|
@ -4,11 +4,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/sentinel"
|
"github.com/hashicorp/consul/sentinel"
|
||||||
"github.com/hashicorp/hcl"
|
"github.com/hashicorp/hcl"
|
||||||
"github.com/hashicorp/hcl/hcl/ast"
|
"github.com/hashicorp/hcl/hcl/ast"
|
||||||
hclprinter "github.com/hashicorp/hcl/hcl/printer"
|
hclprinter "github.com/hashicorp/hcl/hcl/printer"
|
||||||
|
"github.com/hashicorp/hcl/hcl/token"
|
||||||
"golang.org/x/crypto/blake2b"
|
"golang.org/x/crypto/blake2b"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -818,22 +820,36 @@ func TranslateLegacyRules(policyBytes []byte) ([]byte, error) {
|
||||||
|
|
||||||
rewritten := ast.Walk(parsed, func(node ast.Node) (ast.Node, bool) {
|
rewritten := ast.Walk(parsed, func(node ast.Node) (ast.Node, bool) {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *ast.ObjectKey:
|
case *ast.ObjectItem:
|
||||||
switch n.Token.Text {
|
if len(n.Keys) < 1 {
|
||||||
|
return node, true
|
||||||
|
}
|
||||||
|
|
||||||
|
txt := n.Keys[0].Token.Text
|
||||||
|
if n.Keys[0].Token.Type == token.STRING {
|
||||||
|
txt, err = strconv.Unquote(txt)
|
||||||
|
if err != nil {
|
||||||
|
return node, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch txt {
|
||||||
|
case "policy":
|
||||||
|
n.Keys[0].Token.Text = "policy"
|
||||||
case "agent":
|
case "agent":
|
||||||
n.Token.Text = "agent_prefix"
|
n.Keys[0].Token.Text = "agent_prefix"
|
||||||
case "key":
|
case "key":
|
||||||
n.Token.Text = "key_prefix"
|
n.Keys[0].Token.Text = "key_prefix"
|
||||||
case "node":
|
case "node":
|
||||||
n.Token.Text = "node_prefix"
|
n.Keys[0].Token.Text = "node_prefix"
|
||||||
case "query":
|
case "query":
|
||||||
n.Token.Text = "query_prefix"
|
n.Keys[0].Token.Text = "query_prefix"
|
||||||
case "service":
|
case "service":
|
||||||
n.Token.Text = "service_prefix"
|
n.Keys[0].Token.Text = "service_prefix"
|
||||||
case "session":
|
case "session":
|
||||||
n.Token.Text = "session_prefix"
|
n.Keys[0].Token.Text = "session_prefix"
|
||||||
case "event":
|
case "event":
|
||||||
n.Token.Text = "event_prefix"
|
n.Keys[0].Token.Text = "event_prefix"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1407,6 +1407,114 @@ operator = "write"
|
||||||
require.Equal(t, strings.Trim(expected, "\n"), string(output))
|
require.Equal(t, strings.Trim(expected, "\n"), string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRulesTranslate_GH5493(t *testing.T) {
|
||||||
|
input := `
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"policy": "read"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"policy": "read"
|
||||||
|
},
|
||||||
|
"privatething1/": {
|
||||||
|
"policy": "deny"
|
||||||
|
},
|
||||||
|
"anapplication/private/": {
|
||||||
|
"policy": "deny"
|
||||||
|
},
|
||||||
|
"privatething2/": {
|
||||||
|
"policy": "deny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"": {
|
||||||
|
"policy": "write"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"": {
|
||||||
|
"policy": "read"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
expected := `
|
||||||
|
key_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
key_prefix "key" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
key_prefix "policy" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
key_prefix "privatething1/" {
|
||||||
|
policy = "deny"
|
||||||
|
}
|
||||||
|
|
||||||
|
key_prefix "anapplication/private/" {
|
||||||
|
policy = "deny"
|
||||||
|
}
|
||||||
|
|
||||||
|
key_prefix "privatething2/" {
|
||||||
|
policy = "deny"
|
||||||
|
}
|
||||||
|
|
||||||
|
session_prefix "" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
|
|
||||||
|
node_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
agent_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
service_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
event_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
|
||||||
|
query_prefix "" {
|
||||||
|
policy = "read"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
output, err := TranslateLegacyRules([]byte(input))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, strings.Trim(expected, "\n"), string(output))
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrecedence(t *testing.T) {
|
func TestPrecedence(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
@ -53,6 +53,11 @@ func (c *cmd) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.tokenSecret && c.tokenAccessor {
|
||||||
|
c.UI.Error(fmt.Sprintf("Error - cannot specify both -token-secret and -token-accessor"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
data, err := c.dataFromArgs(c.flags.Args())
|
data, err := c.dataFromArgs(c.flags.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error! %v", err))
|
c.UI.Error(fmt.Sprintf("Error! %v", err))
|
||||||
|
@ -69,6 +74,8 @@ func (c *cmd) Run(args []string) int {
|
||||||
// Trim whitespace and newlines (e.g. from echo without -n)
|
// Trim whitespace and newlines (e.g. from echo without -n)
|
||||||
data = strings.TrimSpace(data)
|
data = strings.TrimSpace(data)
|
||||||
|
|
||||||
|
// It is not a bug that this doesn't look at tokenAccessor. We already know that we want the rules from
|
||||||
|
// a token and just need to tell the helper function whether it should be retrieved by its secret or accessor
|
||||||
if rules, err := aclhelpers.GetRulesFromLegacyToken(client, data, c.tokenSecret); err != nil {
|
if rules, err := aclhelpers.GetRulesFromLegacyToken(client, data, c.tokenSecret); err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent"
|
"github.com/hashicorp/consul/agent"
|
||||||
"github.com/hashicorp/consul/logger"
|
"github.com/hashicorp/consul/logger"
|
||||||
"github.com/hashicorp/consul/testrpc"
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
"github.com/hashicorp/consul/testrpc"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRulesTranslateCommand_noTabs(t *testing.T) {
|
func TestRulesTranslateCommand_noTabs(t *testing.T) {
|
||||||
|
@ -25,7 +25,6 @@ func TestRulesTranslateCommand_noTabs(t *testing.T) {
|
||||||
|
|
||||||
func TestRulesTranslateCommand(t *testing.T) {
|
func TestRulesTranslateCommand(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
testDir := testutil.TempDir(t, "acl")
|
testDir := testutil.TempDir(t, "acl")
|
||||||
defer os.RemoveAll(testDir)
|
defer os.RemoveAll(testDir)
|
||||||
|
@ -53,9 +52,9 @@ func TestRulesTranslateCommand(t *testing.T) {
|
||||||
expected := "service_prefix \"\" {\n policy = \"write\"\n}"
|
expected := "service_prefix \"\" {\n policy = \"write\"\n}"
|
||||||
|
|
||||||
// From a file
|
// From a file
|
||||||
{
|
t.Run("file", func(t *testing.T) {
|
||||||
err := ioutil.WriteFile(testDir+"/rules.hcl", []byte(rules), 0644)
|
err := ioutil.WriteFile(testDir+"/rules.hcl", []byte(rules), 0644)
|
||||||
assert.NoError(err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"-http-addr=" + a.HTTPAddr(),
|
"-http-addr=" + a.HTTPAddr(),
|
||||||
|
@ -64,13 +63,13 @@ func TestRulesTranslateCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
code := cmd.Run(args)
|
code := cmd.Run(args)
|
||||||
assert.Equal(code, 0)
|
require.Equal(t, code, 0)
|
||||||
assert.Empty(ui.ErrorWriter.String())
|
require.Empty(t, ui.ErrorWriter.String())
|
||||||
assert.Contains(ui.OutputWriter.String(), expected)
|
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||||
}
|
})
|
||||||
|
|
||||||
// From stdin
|
// From stdin
|
||||||
{
|
t.Run("stdin", func(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
stdinW.Write([]byte(rules))
|
stdinW.Write([]byte(rules))
|
||||||
stdinW.Close()
|
stdinW.Close()
|
||||||
|
@ -83,13 +82,13 @@ func TestRulesTranslateCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
code := cmd.Run(args)
|
code := cmd.Run(args)
|
||||||
assert.Equal(code, 0)
|
require.Equal(t, code, 0)
|
||||||
assert.Empty(ui.ErrorWriter.String())
|
require.Empty(t, ui.ErrorWriter.String())
|
||||||
assert.Contains(ui.OutputWriter.String(), expected)
|
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||||
}
|
})
|
||||||
|
|
||||||
// From arg
|
// From arg
|
||||||
{
|
t.Run("arg", func(t *testing.T) {
|
||||||
args := []string{
|
args := []string{
|
||||||
"-http-addr=" + a.HTTPAddr(),
|
"-http-addr=" + a.HTTPAddr(),
|
||||||
"-token=root",
|
"-token=root",
|
||||||
|
@ -97,8 +96,23 @@ func TestRulesTranslateCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
code := cmd.Run(args)
|
code := cmd.Run(args)
|
||||||
assert.Equal(code, 0)
|
require.Equal(t, code, 0)
|
||||||
assert.Empty(ui.ErrorWriter.String())
|
require.Empty(t, ui.ErrorWriter.String())
|
||||||
assert.Contains(ui.OutputWriter.String(), expected)
|
require.Contains(t, ui.OutputWriter.String(), expected)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
// cannot specify both secret and accessor
|
||||||
|
t.Run("exclusive-options", func(t *testing.T) {
|
||||||
|
args := []string{
|
||||||
|
"-http-addr=" + a.HTTPAddr(),
|
||||||
|
"-token=root",
|
||||||
|
"-token-secret",
|
||||||
|
"-token-accessor",
|
||||||
|
`token "" { policy = "write" }`,
|
||||||
|
}
|
||||||
|
|
||||||
|
code := cmd.Run(args)
|
||||||
|
require.Equal(t, 1, code, 0)
|
||||||
|
require.Equal(t, "Error - cannot specify both -token-secret and -token-accessor\n", ui.ErrorWriter.String())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue