You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
consul/command/acl/token/formatter.go

372 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package token
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api"
)
const (
PrettyFormat string = "pretty"
JSONFormat string = "json"
WHITESPACE_2 string = "\t"
WHITESPACE_4 string = "\t\t"
)
// Formatter defines methods provided by token command output formatter
type Formatter interface {
FormatToken(token *api.ACLToken) (string, error)
FormatTokenExpanded(token *api.ACLTokenExpanded) (string, error)
FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error)
}
// GetSupportedFormats returns supported formats
func GetSupportedFormats() []string {
return []string{PrettyFormat, JSONFormat}
}
// NewFormatter returns Formatter implementation
func NewFormatter(format string, showMeta bool) (formatter Formatter, err error) {
switch format {
case PrettyFormat:
formatter = newPrettyFormatter(showMeta)
case JSONFormat:
formatter = newJSONFormatter(showMeta)
default:
err = fmt.Errorf("Unknown format: %s", format)
}
return formatter, err
}
func newPrettyFormatter(showMeta bool) Formatter {
return &prettyFormatter{showMeta}
}
type prettyFormatter struct {
showMeta bool
}
func (f *prettyFormatter) FormatToken(token *api.ACLToken) (string, error) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("AccessorID: %s\n", token.AccessorID))
buffer.WriteString(fmt.Sprintf("SecretID: %s\n", token.SecretID))
if token.Partition != "" {
buffer.WriteString(fmt.Sprintf("Partition: %s\n", token.Partition))
}
if token.Namespace != "" {
buffer.WriteString(fmt.Sprintf("Namespace: %s\n", token.Namespace))
}
buffer.WriteString(fmt.Sprintf("Description: %s\n", token.Description))
buffer.WriteString(fmt.Sprintf("Local: %t\n", token.Local))
if token.AuthMethod != "" {
buffer.WriteString(fmt.Sprintf("Auth Method: %s (Namespace: %s)\n", token.AuthMethod, token.AuthMethodNamespace))
}
buffer.WriteString(fmt.Sprintf("Create Time: %v\n", token.CreateTime))
if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() {
buffer.WriteString(fmt.Sprintf("Expiration Time: %v\n", *token.ExpirationTime))
}
if f.showMeta {
buffer.WriteString(fmt.Sprintf("Hash: %x\n", token.Hash))
buffer.WriteString(fmt.Sprintf("Create Index: %d\n", token.CreateIndex))
buffer.WriteString(fmt.Sprintf("Modify Index: %d\n", token.ModifyIndex))
}
if len(token.Policies) > 0 {
buffer.WriteString(fmt.Sprintln("Policies:"))
for _, policy := range token.Policies {
buffer.WriteString(fmt.Sprintf(" %s - %s\n", policy.ID, policy.Name))
}
}
if len(token.Roles) > 0 {
buffer.WriteString(fmt.Sprintln("Roles:"))
for _, role := range token.Roles {
buffer.WriteString(fmt.Sprintf(" %s - %s\n", role.ID, role.Name))
}
}
if len(token.ServiceIdentities) > 0 {
buffer.WriteString(fmt.Sprintln("Service Identities:"))
for _, svcid := range token.ServiceIdentities {
if len(svcid.Datacenters) > 0 {
buffer.WriteString(fmt.Sprintf(" %s (Datacenters: %s)\n", svcid.ServiceName, strings.Join(svcid.Datacenters, ", ")))
} else {
buffer.WriteString(fmt.Sprintf(" %s (Datacenters: all)\n", svcid.ServiceName))
}
}
}
if len(token.NodeIdentities) > 0 {
buffer.WriteString(fmt.Sprintln("Node Identities:"))
for _, nodeid := range token.NodeIdentities {
buffer.WriteString(fmt.Sprintf(" %s (Datacenter: %s)\n", nodeid.NodeName, nodeid.Datacenter))
}
}
return buffer.String(), nil
}
func (f *prettyFormatter) FormatTokenExpanded(token *api.ACLTokenExpanded) (string, error) {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("AccessorID: %s\n", token.AccessorID))
buffer.WriteString(fmt.Sprintf("SecretID: %s\n", token.SecretID))
if token.Partition != "" {
buffer.WriteString(fmt.Sprintf("Partition: %s\n", token.Partition))
}
if token.Namespace != "" {
buffer.WriteString(fmt.Sprintf("Namespace: %s\n", token.Namespace))
}
buffer.WriteString(fmt.Sprintf("Description: %s\n", token.Description))
buffer.WriteString(fmt.Sprintf("Local: %t\n", token.Local))
if token.AuthMethod != "" {
buffer.WriteString(fmt.Sprintf("Auth Method: %s (Namespace: %s)\n", token.AuthMethod, token.AuthMethodNamespace))
}
buffer.WriteString(fmt.Sprintf("Create Time: %v\n", token.CreateTime))
if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() {
buffer.WriteString(fmt.Sprintf("Expiration Time: %v\n", *token.ExpirationTime))
}
if f.showMeta {
buffer.WriteString(fmt.Sprintf("Hash: %x\n", token.Hash))
buffer.WriteString(fmt.Sprintf("Create Index: %d\n", token.CreateIndex))
buffer.WriteString(fmt.Sprintf("Modify Index: %d\n", token.ModifyIndex))
}
policies := make(map[string]api.ACLPolicy)
roles := make(map[string]api.ACLRole)
for _, policy := range token.ExpandedPolicies {
policies[policy.ID] = policy
}
for _, role := range token.ExpandedRoles {
roles[role.ID] = role
}
formatPolicy := func(policy api.ACLPolicy, indent string) {
buffer.WriteString(fmt.Sprintf(indent+"Policy Name: %s\n", policy.Name))
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"ID: %s\n", policy.ID))
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"Description: %s\n", policy.Description))
buffer.WriteString(indent + WHITESPACE_2 + "Rules:\n")
buffer.WriteString(indent + WHITESPACE_4)
buffer.WriteString(strings.ReplaceAll(policy.Rules, "\n", "\n"+indent+WHITESPACE_4))
buffer.WriteString("\n\n")
}
if len(token.ACLToken.Policies) > 0 {
buffer.WriteString("Policies:\n")
for _, policyLink := range token.ACLToken.Policies {
formatPolicy(policies[policyLink.ID], WHITESPACE_2)
}
}
entMeta := acl.NewEnterpriseMetaWithPartition(token.Partition, token.Namespace)
formatServiceIdentity := func(svcIdentity *api.ACLServiceIdentity, indent string) {
if len(svcIdentity.Datacenters) > 0 {
buffer.WriteString(fmt.Sprintf(indent+"Name: %s (Datacenters: %s)\n", svcIdentity.ServiceName, strings.Join(svcIdentity.Datacenters, ", ")))
} else {
buffer.WriteString(fmt.Sprintf(indent+"Name: %s (Datacenters: all)\n", svcIdentity.ServiceName))
}
identity := structs.ACLServiceIdentity{ServiceName: svcIdentity.ServiceName, Datacenters: svcIdentity.Datacenters}
policy := identity.SyntheticPolicy(&entMeta)
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"Description: %s\n", policy.Description))
buffer.WriteString(indent + WHITESPACE_2 + "Rules:")
buffer.WriteString(strings.ReplaceAll(policy.Rules, "\n", "\n"+indent+WHITESPACE_4))
buffer.WriteString("\n\n")
}
if len(token.ACLToken.ServiceIdentities) > 0 {
buffer.WriteString("Service Identities:\n")
for _, svcIdentity := range token.ACLToken.ServiceIdentities {
formatServiceIdentity(svcIdentity, WHITESPACE_2)
}
}
formatNodeIdentity := func(nodeIdentity *api.ACLNodeIdentity, indent string) {
buffer.WriteString(fmt.Sprintf(indent+"Name: %s (Datacenter: %s)\n", nodeIdentity.NodeName, nodeIdentity.Datacenter))
identity := structs.ACLNodeIdentity{NodeName: nodeIdentity.NodeName, Datacenter: nodeIdentity.Datacenter}
policy := identity.SyntheticPolicy(&entMeta)
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"Description: %s\n", policy.Description))
buffer.WriteString(indent + WHITESPACE_2 + "Rules:")
buffer.WriteString(strings.ReplaceAll(policy.Rules, "\n", "\n"+indent+WHITESPACE_4))
buffer.WriteString("\n\n")
}
if len(token.ACLToken.NodeIdentities) > 0 {
buffer.WriteString("Node Identities:\n")
for _, nodeIdentity := range token.ACLToken.NodeIdentities {
formatNodeIdentity(nodeIdentity, WHITESPACE_2)
}
}
formatRole := func(role api.ACLRole, indent string) {
buffer.WriteString(fmt.Sprintf(indent+"Role Name: %s\n", role.Name))
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"ID: %s\n", role.ID))
buffer.WriteString(fmt.Sprintf(indent+WHITESPACE_2+"Description: %s\n", role.Description))
if len(role.Policies) > 0 {
buffer.WriteString(indent + WHITESPACE_2 + "Policies:\n")
for _, policyLink := range role.Policies {
formatPolicy(policies[policyLink.ID], indent+WHITESPACE_4)
}
}
if len(role.ServiceIdentities) > 0 {
buffer.WriteString(indent + WHITESPACE_2 + "Service Identities:\n")
for _, svcIdentity := range role.ServiceIdentities {
formatServiceIdentity(svcIdentity, indent+WHITESPACE_4)
}
}
if len(role.NodeIdentities) > 0 {
buffer.WriteString(indent + WHITESPACE_2 + "Node Identities:\n")
for _, nodeIdentity := range role.NodeIdentities {
formatNodeIdentity(nodeIdentity, indent+WHITESPACE_4)
}
}
}
if len(token.ACLToken.Roles) > 0 {
buffer.WriteString("Roles:\n")
for _, roleLink := range token.ACLToken.Roles {
role := roles[roleLink.ID]
formatRole(role, WHITESPACE_2)
}
}
buffer.WriteString("=== End of Authorizer Layer 0: Token ===\n")
if len(token.NamespaceDefaultPolicyIDs) > 0 || len(token.NamespaceDefaultRoleIDs) > 0 {
buffer.WriteString("=== Start of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===\n")
buffer.WriteString(fmt.Sprintf("Description: ACL Roles inherited by all Tokens in Namespace %q\n\n", token.Namespace))
buffer.WriteString("Namespace Policy Defaults:\n")
for _, policyID := range token.NamespaceDefaultPolicyIDs {
formatPolicy(policies[policyID], WHITESPACE_2)
}
buffer.WriteString("Namespace Role Defaults:\n")
for _, roleID := range token.NamespaceDefaultRoleIDs {
formatRole(roles[roleID], WHITESPACE_2)
}
buffer.WriteString("=== End of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===\n")
}
buffer.WriteString("=== Start of Authorizer Layer 2: Agent Configuration Defaults (Inherited) ===\n")
buffer.WriteString("Description: Defined at request-time by the agent that resolves the ACL token; other agents may have different configuration defaults\n")
buffer.WriteString(fmt.Sprintf("Resolved By Agent: %q\n\n", token.ResolvedByAgent))
buffer.WriteString(fmt.Sprintf("Default Policy: %s\n", token.AgentACLDefaultPolicy))
buffer.WriteString(WHITESPACE_2 + "Description: Backstop rule used if no preceding layer has a matching rule (refer to default_policy option in agent configuration)\n\n")
buffer.WriteString(fmt.Sprintf("Down Policy: %s\n", token.AgentACLDownPolicy))
buffer.WriteString(WHITESPACE_2 + "Description: Defines what to do if this Token's information cannot be read from the primary_datacenter (refer to down_policy option in agent configuration)\n\n")
return buffer.String(), nil
}
func (f *prettyFormatter) FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error) {
var buffer bytes.Buffer
first := true
for _, token := range tokens {
if first {
first = false
} else {
buffer.WriteString("\n")
}
buffer.WriteString(f.formatTokenListEntry(token))
}
return buffer.String(), nil
}
func (f *prettyFormatter) formatTokenListEntry(token *api.ACLTokenListEntry) string {
var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("AccessorID: %s\n", token.AccessorID))
buffer.WriteString(fmt.Sprintf("SecretID: %s\n", token.SecretID))
if token.Partition != "" {
buffer.WriteString(fmt.Sprintf("Partition: %s\n", token.Partition))
}
if token.Namespace != "" {
buffer.WriteString(fmt.Sprintf("Namespace: %s\n", token.Namespace))
}
buffer.WriteString(fmt.Sprintf("Description: %s\n", token.Description))
buffer.WriteString(fmt.Sprintf("Local: %t\n", token.Local))
if token.AuthMethod != "" {
buffer.WriteString(fmt.Sprintf("Auth Method: %s (Namespace: %s)\n", token.AuthMethod, token.AuthMethodNamespace))
}
buffer.WriteString(fmt.Sprintf("Create Time: %v\n", token.CreateTime))
if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() {
buffer.WriteString(fmt.Sprintf("Expiration Time: %v\n", *token.ExpirationTime))
}
if f.showMeta {
buffer.WriteString(fmt.Sprintf("Hash: %x\n", token.Hash))
buffer.WriteString(fmt.Sprintf("Create Index: %d\n", token.CreateIndex))
buffer.WriteString(fmt.Sprintf("Modify Index: %d\n", token.ModifyIndex))
}
if len(token.Policies) > 0 {
buffer.WriteString(fmt.Sprintln("Policies:"))
for _, policy := range token.Policies {
buffer.WriteString(fmt.Sprintf(" %s - %s\n", policy.ID, policy.Name))
}
}
if len(token.Roles) > 0 {
buffer.WriteString(fmt.Sprintln("Roles:"))
for _, role := range token.Roles {
buffer.WriteString(fmt.Sprintf(" %s - %s\n", role.ID, role.Name))
}
}
if len(token.ServiceIdentities) > 0 {
buffer.WriteString(fmt.Sprintln("Service Identities:"))
for _, svcid := range token.ServiceIdentities {
if len(svcid.Datacenters) > 0 {
buffer.WriteString(fmt.Sprintf(" %s (Datacenters: %s)\n", svcid.ServiceName, strings.Join(svcid.Datacenters, ", ")))
} else {
buffer.WriteString(fmt.Sprintf(" %s (Datacenters: all)\n", svcid.ServiceName))
}
}
}
if len(token.NodeIdentities) > 0 {
buffer.WriteString(fmt.Sprintln("Node Identities:"))
for _, nodeid := range token.NodeIdentities {
buffer.WriteString(fmt.Sprintf(" %s (Datacenter: %s)\n", nodeid.NodeName, nodeid.Datacenter))
}
}
return buffer.String()
}
func newJSONFormatter(showMeta bool) Formatter {
return &jsonFormatter{showMeta}
}
type jsonFormatter struct {
showMeta bool
}
func (f *jsonFormatter) FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error) {
b, err := json.MarshalIndent(tokens, "", " ")
if err != nil {
return "", fmt.Errorf("Failed to marshal tokens: %v", err)
}
return string(b), nil
}
func (f *jsonFormatter) FormatToken(token *api.ACLToken) (string, error) {
b, err := json.MarshalIndent(token, "", " ")
if err != nil {
return "", fmt.Errorf("Failed to marshal token: %v", err)
}
return string(b), nil
}
func (f *jsonFormatter) FormatTokenExpanded(token *api.ACLTokenExpanded) (string, error) {
b, err := json.MarshalIndent(token, "", " ")
if err != nil {
return "", fmt.Errorf("Failed to marshal token: %v", err)
}
return string(b), nil
}