consul/command/acl/authmethod/create/authmethod_create_test.go

637 lines
14 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package authmethodcreate
import (
"encoding/json"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/hashicorp/go-uuid"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/acl"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/testrpc"
// activate testing auth method
_ "github.com/hashicorp/consul/agent/consul/authmethod/testauth"
)
func TestAuthMethodCreateCommand_noTabs(t *testing.T) {
t.Parallel()
if strings.ContainsRune(New(cli.NewMockUi()).Help(), '\t') {
t.Fatal("help has tabs")
}
}
func TestAuthMethodCreateCommand(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.NewTestAgent(t, `
primary_datacenter = "dc1"
acl {
enabled = true
tokens {
initial_management = "root"
}
}`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1", testrpc.WithToken("root"))
client := a.Client()
t.Run("type required", func(t *testing.T) {
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-type' flag")
})
t.Run("name required", func(t *testing.T) {
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-name' flag")
})
t.Run("invalid type", func(t *testing.T) {
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=invalid",
"-name=my-method",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Invalid Auth Method: Type should be one of")
})
t.Run("create testing", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 0)
require.Empty(t, ui.ErrorWriter.String())
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
}
require.Equal(t, expect, got)
})
t.Run("create testing with max token ttl", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
"-max-token-ttl=5m",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 0, "err: "+ui.ErrorWriter.String())
require.Empty(t, ui.ErrorWriter.String())
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
MaxTokenTTL: 5 * time.Minute,
}
require.Equal(t, expect, got)
})
t.Run("create testing with token type global", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
"-token-locality=global",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 0, "err: "+ui.ErrorWriter.String())
require.Empty(t, ui.ErrorWriter.String())
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
TokenLocality: "global",
}
require.Equal(t, expect, got)
})
}
func TestAuthMethodCreateCommand_JSON(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.NewTestAgent(t, `
primary_datacenter = "dc1"
acl {
enabled = true
tokens {
initial_management = "root"
}
}`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1", testrpc.WithToken("root"))
client := a.Client()
t.Run("type required", func(t *testing.T) {
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-format=json",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-type' flag")
})
t.Run("create testing", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
"-format=json",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
out := ui.OutputWriter.String()
require.Equal(t, code, 0)
require.Empty(t, ui.ErrorWriter.String())
require.Contains(t, out, name)
var jsonOutput json.RawMessage
require.NoError(t, json.Unmarshal([]byte(out), &jsonOutput))
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
}
require.Equal(t, expect, got)
})
t.Run("create testing with max token ttl", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
"-max-token-ttl=5m",
"-format=json",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
out := ui.OutputWriter.String()
require.Equal(t, code, 0)
require.Empty(t, ui.ErrorWriter.String())
require.Contains(t, out, name)
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
MaxTokenTTL: 5 * time.Minute,
}
require.Equal(t, expect, got)
var raw map[string]interface{}
require.NoError(t, json.Unmarshal([]byte(out), &raw))
delete(raw, "CreateIndex")
delete(raw, "ModifyIndex")
delete(raw, "Namespace")
delete(raw, "Partition")
require.Equal(t, map[string]interface{}{
"Name": name,
"Type": "testing",
"DisplayName": "display",
"Description": "desc",
"MaxTokenTTL": "5m0s",
"Config": nil,
}, raw)
})
t.Run("create testing with token type global", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-description=desc",
"-display-name=display",
"-token-locality=global",
"-format=json",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
out := ui.OutputWriter.String()
require.Equal(t, code, 0)
require.Empty(t, ui.ErrorWriter.String())
require.Contains(t, out, name)
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "testing",
DisplayName: "display",
Description: "desc",
TokenLocality: "global",
}
require.Equal(t, expect, got)
var raw map[string]interface{}
require.NoError(t, json.Unmarshal([]byte(out), &raw))
delete(raw, "CreateIndex")
delete(raw, "ModifyIndex")
delete(raw, "Namespace")
delete(raw, "Partition")
require.Equal(t, map[string]interface{}{
"Name": name,
"Type": "testing",
"DisplayName": "display",
"Description": "desc",
"TokenLocality": "global",
"Config": nil,
}, raw)
})
}
func TestAuthMethodCreateCommand_k8s(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
testDir := testutil.TempDir(t, "acl")
a := agent.NewTestAgent(t, `
primary_datacenter = "dc1"
acl {
enabled = true
tokens {
initial_management = "root"
}
}`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1", testrpc.WithToken("root"))
client := a.Client()
t.Run("k8s host required", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=kubernetes",
"-name", name,
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-kubernetes-host' flag")
})
t.Run("k8s ca cert required", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=kubernetes",
"-name", name,
"-kubernetes-host=https://foo.internal:8443",
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-kubernetes-ca-cert' flag")
})
ca := connect.TestCA(t, nil)
t.Run("k8s jwt required", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=kubernetes",
"-name", name,
"-kubernetes-host=https://foo.internal:8443",
"-kubernetes-ca-cert", ca.RootCert,
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 1)
require.Contains(t, ui.ErrorWriter.String(), "Missing required '-kubernetes-service-account-jwt' flag")
})
t.Run("create k8s", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=kubernetes",
"-name", name,
"-kubernetes-host", "https://foo.internal:8443",
"-kubernetes-ca-cert", ca.RootCert,
"-kubernetes-service-account-jwt", acl.TestKubernetesJWT_A,
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, 0, code)
require.Empty(t, ui.ErrorWriter.String())
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "kubernetes",
Config: map[string]interface{}{
"Host": "https://foo.internal:8443",
"CACert": ca.RootCert,
"ServiceAccountJWT": acl.TestKubernetesJWT_A,
},
}
require.Equal(t, expect, got)
})
caFile := filepath.Join(testDir, "ca.crt")
require.NoError(t, os.WriteFile(caFile, []byte(ca.RootCert), 0600))
t.Run("create k8s with cert file", func(t *testing.T) {
name := getTestName(t)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=kubernetes",
"-name", name,
"-kubernetes-host", "https://foo.internal:8443",
"-kubernetes-ca-cert", "@" + caFile,
"-kubernetes-service-account-jwt", acl.TestKubernetesJWT_A,
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, code, 0)
require.Empty(t, ui.ErrorWriter.String())
got := getTestMethod(t, client, name)
expect := &api.ACLAuthMethod{
Name: name,
Type: "kubernetes",
Config: map[string]interface{}{
"Host": "https://foo.internal:8443",
"CACert": ca.RootCert,
"ServiceAccountJWT": acl.TestKubernetesJWT_A,
},
}
require.Equal(t, expect, got)
})
}
func TestAuthMethodCreateCommand_config(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
testDir := testutil.TempDir(t, "auth-method")
a := agent.NewTestAgent(t, `
primary_datacenter = "dc1"
acl {
enabled = true
tokens {
initial_management = "root"
}
}`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1", testrpc.WithToken("root"))
client := a.Client()
checkMethod := func(t *testing.T, methodName string) {
method, _, err := client.ACL().AuthMethodRead(
methodName,
&api.QueryOptions{Token: "root"},
)
require.NoError(t, err)
require.NotNil(t, method)
require.Equal(t, "foo", method.Config["SessionID"])
}
t.Run("config file", func(t *testing.T) {
name := getTestName(t)
configFile := filepath.Join(testDir, "config.json")
jsonConfig := `{"SessionID":"foo"}`
require.NoError(t, os.WriteFile(configFile, []byte(jsonConfig), 0644))
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-config=@" + configFile,
}
ui := cli.NewMockUi()
cmd := New(ui)
code := cmd.Run(args)
require.Equal(t, 0, code)
require.Empty(t, ui.ErrorWriter.String())
checkMethod(t, name)
})
t.Run("config std-in", func(t *testing.T) {
name := getTestName(t)
stdinR, stdinW := io.Pipe()
ui := cli.NewMockUi()
cmd := New(ui)
cmd.testStdin = stdinR
go func() {
stdinW.Write([]byte(`{"SessionID":"foo"}`))
stdinW.Close()
}()
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-config=-",
}
code := cmd.Run(args)
require.Equal(t, 0, code)
require.Empty(t, ui.ErrorWriter.String())
checkMethod(t, name)
})
t.Run("config string", func(t *testing.T) {
name := getTestName(t)
ui := cli.NewMockUi()
cmd := New(ui)
args := []string{
"-http-addr=" + a.HTTPAddr(),
"-token=root",
"-type=testing",
"-name", name,
"-config=" + `{"SessionID":"foo"}`,
}
code := cmd.Run(args)
require.Equal(t, 0, code)
require.Empty(t, ui.ErrorWriter.String())
checkMethod(t, name)
})
}
func getTestMethod(t *testing.T, client *api.Client, methodName string) *api.ACLAuthMethod {
t.Helper()
method, _, err := client.ACL().AuthMethodRead(
methodName,
&api.QueryOptions{Token: "root"},
)
require.NoError(t, err)
require.NotNil(t, method)
// zero these out since we don't really care
method.CreateIndex = 0
method.ModifyIndex = 0
if method.Namespace == "default" {
method.Namespace = ""
}
if method.Partition == "default" {
method.Partition = ""
}
return method
}
func getTestName(t *testing.T) string {
t.Helper()
id, err := uuid.GenerateUUID()
require.NoError(t, err)
return "test-" + id
}