Add expanded token read flag and endpoint option

This commit is contained in:
Kyle Havlovitz 2022-03-31 09:49:11 -07:00
parent aae6d8080d
commit 9a2474381a
19 changed files with 1423 additions and 6 deletions

3
.changelog/12670.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
cli: The `token read` command now supports the `-expanded` flag to display detailed role and policy information for the token.
```

View File

@ -378,6 +378,9 @@ func (s *HTTPHandlers) ACLTokenGet(resp http.ResponseWriter, req *http.Request,
if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil { if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil {
return nil, err return nil, err
} }
if _, ok := req.URL.Query()["expanded"]; ok {
args.Expanded = true
}
if args.Datacenter == "" { if args.Datacenter == "" {
args.Datacenter = s.agent.config.Datacenter args.Datacenter = s.agent.config.Datacenter
@ -393,6 +396,14 @@ func (s *HTTPHandlers) ACLTokenGet(resp http.ResponseWriter, req *http.Request,
return nil, acl.ErrNotFound return nil, acl.ErrNotFound
} }
if args.Expanded {
expanded := &structs.ACLTokenExpanded{
ACLToken: out.Token,
ExpandedTokenInfo: out.ExpandedTokenInfo,
}
return expanded, nil
}
return out.Token, nil return out.Token, nil
} }

View File

@ -724,6 +724,17 @@ func TestACL_HTTP(t *testing.T) {
require.True(t, ok) require.True(t, ok)
require.Equal(t, expected, token) require.Equal(t, expected, token)
}) })
t.Run("Read-expanded", func(t *testing.T) {
expected := tokenMap[idMap["token-test"]]
req, _ := http.NewRequest("GET", "/v1/acl/token/"+expected.AccessorID+"?token=root&expanded=true", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.ACLTokenCRUD(resp, req)
require.NoError(t, err)
tokenResp, ok := obj.(*structs.ACLTokenExpanded)
require.True(t, ok)
require.Equal(t, expected, tokenResp.ACLToken)
require.Len(t, tokenResp.ExpandedPolicies, 3)
})
t.Run("Self", func(t *testing.T) { t.Run("Self", func(t *testing.T) {
expected := tokenMap[idMap["token-test"]] expected := tokenMap[idMap["token-test"]]
req, _ := http.NewRequest("GET", "/v1/acl/token/self?token="+expected.SecretID, nil) req, _ := http.NewRequest("GET", "/v1/acl/token/self?token="+expected.SecretID, nil)

View File

@ -325,10 +325,99 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke
if token == nil { if token == nil {
return errNotFound return errNotFound
} }
if args.Expanded {
info, err := a.lookupExpandedTokenInfo(ws, state, token)
if err != nil {
return err
}
reply.ExpandedTokenInfo = info
}
return nil return nil
}) })
} }
func (a *ACL) lookupExpandedTokenInfo(ws memdb.WatchSet, state *state.Store, token *structs.ACLToken) (structs.ExpandedTokenInfo, error) {
policyIDs := make(map[string]struct{})
roleIDs := make(map[string]struct{})
identityPolicies := make(map[string]*structs.ACLPolicy)
tokenInfo := structs.ExpandedTokenInfo{}
// Add the token's policies and node/service identity policies
for _, policy := range token.Policies {
policyIDs[policy.ID] = struct{}{}
}
for _, roleLink := range token.Roles {
roleIDs[roleLink.ID] = struct{}{}
}
for _, identity := range token.ServiceIdentities {
policy := identity.SyntheticPolicy(&token.EnterpriseMeta)
identityPolicies[policy.ID] = policy
}
for _, identity := range token.NodeIdentities {
policy := identity.SyntheticPolicy(&token.EnterpriseMeta)
identityPolicies[policy.ID] = policy
}
// Get any namespace default roles/policies to look up
nsPolicies, nsRoles, err := getTokenNamespaceDefaults(ws, state, &token.EnterpriseMeta)
if err != nil {
return tokenInfo, err
}
tokenInfo.NamespaceDefaultPolicyIDs = nsPolicies
tokenInfo.NamespaceDefaultRoleIDs = nsRoles
for _, id := range nsPolicies {
policyIDs[id] = struct{}{}
}
for _, id := range nsRoles {
roleIDs[id] = struct{}{}
}
// Add each role's policies and node/service identity policies
for roleID := range roleIDs {
_, role, err := state.ACLRoleGetByID(ws, roleID, &token.EnterpriseMeta)
if err != nil {
return tokenInfo, err
}
for _, policy := range role.Policies {
policyIDs[policy.ID] = struct{}{}
}
for _, identity := range role.ServiceIdentities {
policy := identity.SyntheticPolicy(&role.EnterpriseMeta)
identityPolicies[policy.ID] = policy
}
for _, identity := range role.NodeIdentities {
policy := identity.SyntheticPolicy(&role.EnterpriseMeta)
identityPolicies[policy.ID] = policy
}
tokenInfo.ExpandedRoles = append(tokenInfo.ExpandedRoles, role)
}
var policies []*structs.ACLPolicy
for id := range policyIDs {
_, policy, err := state.ACLPolicyGetByID(ws, id, &token.EnterpriseMeta)
if err != nil {
return tokenInfo, err
}
policies = append(policies, policy)
}
for _, policy := range identityPolicies {
policies = append(policies, policy)
}
tokenInfo.ExpandedPolicies = policies
tokenInfo.AgentACLDefaultPolicy = a.srv.config.ACLResolverSettings.ACLDefaultPolicy
tokenInfo.AgentACLDownPolicy = a.srv.config.ACLResolverSettings.ACLDownPolicy
tokenInfo.ResolvedByAgent = a.srv.config.NodeName
return tokenInfo, nil
}
func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLToken) error { func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLToken) error {
if err := a.aclPreCheck(); err != nil { if err := a.aclPreCheck(); err != nil {
return err return err

View File

@ -5,7 +5,9 @@ package consul
import ( import (
"github.com/hashicorp/consul/agent/consul/authmethod" "github.com/hashicorp/consul/agent/consul/authmethod"
"github.com/hashicorp/consul/agent/consul/state"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
memdb "github.com/hashicorp/go-memdb"
) )
func (a *ACL) tokenUpsertValidateEnterprise(token *structs.ACLToken, existing *structs.ACLToken) error { func (a *ACL) tokenUpsertValidateEnterprise(token *structs.ACLToken, existing *structs.ACLToken) error {
@ -37,3 +39,7 @@ func computeTargetEnterpriseMeta(
) (*structs.EnterpriseMeta, error) { ) (*structs.EnterpriseMeta, error) {
return &structs.EnterpriseMeta{}, nil return &structs.EnterpriseMeta{}, nil
} }
func getTokenNamespaceDefaults(ws memdb.WatchSet, state *state.Store, entMeta *structs.EnterpriseMeta) ([]string, []string, error) {
return nil, nil, nil
}

View File

@ -204,6 +204,181 @@ func TestACLEndpoint_TokenRead(t *testing.T) {
require.Nil(t, resp.Token) require.Nil(t, resp.Token)
require.EqualError(t, err, "failed acl token lookup: index error: UUID must be 36 characters") require.EqualError(t, err, "failed acl token lookup: index error: UUID must be 36 characters")
}) })
t.Run("expanded output with role/policy", func(t *testing.T) {
p1, err := upsertTestPolicy(codec, TestDefaultInitialManagementToken, "dc1")
require.NoError(t, err)
p2, err := upsertTestPolicy(codec, TestDefaultInitialManagementToken, "dc1")
require.NoError(t, err)
r1, err := upsertTestCustomizedRole(codec, TestDefaultInitialManagementToken, "dc1", func(role *structs.ACLRole) {
role.Policies = []structs.ACLRolePolicyLink{
{
ID: p2.ID,
},
}
})
require.NoError(t, err)
setReq := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
Description: "foobar",
Policies: []structs.ACLTokenPolicyLink{
{
ID: p1.ID,
},
},
Roles: []structs.ACLTokenRoleLink{
{
ID: r1.ID,
},
},
Local: false,
},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
setResp := structs.ACLToken{}
err = msgpackrpc.CallWithCodec(codec, "ACL.TokenSet", &setReq, &setResp)
require.NoError(t, err)
require.NotEmpty(t, setResp.AccessorID)
req := structs.ACLTokenGetRequest{
Datacenter: "dc1",
TokenID: setResp.AccessorID,
TokenIDType: structs.ACLTokenAccessor,
Expanded: true,
QueryOptions: structs.QueryOptions{Token: TestDefaultInitialManagementToken},
}
resp := structs.ACLTokenResponse{}
err = msgpackrpc.CallWithCodec(codec, "ACL.TokenRead", &req, &resp)
require.NoError(t, err)
require.NotNil(t, resp.Token)
require.ElementsMatch(t, []*structs.ACLPolicy{p1, p2}, resp.ExpandedPolicies)
require.ElementsMatch(t, []*structs.ACLRole{r1}, resp.ExpandedRoles)
})
t.Run("expanded output with multiple roles that share a policy", func(t *testing.T) {
p1, err := upsertTestPolicy(codec, TestDefaultInitialManagementToken, "dc1")
require.NoError(t, err)
r1, err := upsertTestCustomizedRole(codec, TestDefaultInitialManagementToken, "dc1", func(role *structs.ACLRole) {
role.Policies = []structs.ACLRolePolicyLink{
{
ID: p1.ID,
},
}
})
require.NoError(t, err)
r2, err := upsertTestCustomizedRole(codec, TestDefaultInitialManagementToken, "dc1", func(role *structs.ACLRole) {
role.Policies = []structs.ACLRolePolicyLink{
{
ID: p1.ID,
},
}
})
require.NoError(t, err)
setReq := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
Description: "foobar",
Roles: []structs.ACLTokenRoleLink{
{
ID: r1.ID,
},
{
ID: r2.ID,
},
},
Local: false,
},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
setResp := structs.ACLToken{}
err = msgpackrpc.CallWithCodec(codec, "ACL.TokenSet", &setReq, &setResp)
require.NoError(t, err)
require.NotEmpty(t, setResp.AccessorID)
req := structs.ACLTokenGetRequest{
Datacenter: "dc1",
TokenID: setResp.AccessorID,
TokenIDType: structs.ACLTokenAccessor,
Expanded: true,
QueryOptions: structs.QueryOptions{Token: TestDefaultInitialManagementToken},
}
resp := structs.ACLTokenResponse{}
err = msgpackrpc.CallWithCodec(codec, "ACL.TokenRead", &req, &resp)
require.NoError(t, err)
require.NotNil(t, resp.Token)
require.ElementsMatch(t, []*structs.ACLPolicy{p1}, resp.ExpandedPolicies)
require.ElementsMatch(t, []*structs.ACLRole{r1, r2}, resp.ExpandedRoles)
})
t.Run("expanded output with node/service identities", func(t *testing.T) {
setReq := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
Description: "foobar",
ServiceIdentities: []*structs.ACLServiceIdentity{
{
ServiceName: "web",
Datacenters: []string{"dc1"},
},
{
ServiceName: "db",
Datacenters: []string{"dc2"},
},
},
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "foo",
Datacenter: "dc1",
},
{
NodeName: "bar",
Datacenter: "dc1",
},
},
Local: false,
},
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
}
var expectedPolicies []*structs.ACLPolicy
entMeta := structs.DefaultEnterpriseMetaInDefaultPartition()
for _, serviceIdentity := range setReq.ACLToken.ServiceIdentities {
expectedPolicies = append(expectedPolicies, serviceIdentity.SyntheticPolicy(entMeta))
}
for _, serviceIdentity := range setReq.ACLToken.NodeIdentities {
expectedPolicies = append(expectedPolicies, serviceIdentity.SyntheticPolicy(entMeta))
}
setResp := structs.ACLToken{}
err := msgpackrpc.CallWithCodec(codec, "ACL.TokenSet", &setReq, &setResp)
require.NoError(t, err)
require.NotEmpty(t, setResp.AccessorID)
req := structs.ACLTokenGetRequest{
Datacenter: "dc1",
TokenID: setResp.AccessorID,
TokenIDType: structs.ACLTokenAccessor,
Expanded: true,
QueryOptions: structs.QueryOptions{Token: TestDefaultInitialManagementToken},
}
resp := structs.ACLTokenResponse{}
err = msgpackrpc.CallWithCodec(codec, "ACL.TokenRead", &req, &resp)
require.NoError(t, err)
require.NotNil(t, resp.Token)
require.ElementsMatch(t, expectedPolicies, resp.ExpandedPolicies)
})
} }
func TestACLEndpoint_TokenClone(t *testing.T) { func TestACLEndpoint_TokenClone(t *testing.T) {

View File

@ -158,7 +158,8 @@ func (s *ACLServiceIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy
policy := &ACLPolicy{} policy := &ACLPolicy{}
policy.ID = hashID policy.ID = hashID
policy.Name = fmt.Sprintf("synthetic-policy-%s", hashID) policy.Name = fmt.Sprintf("synthetic-policy-%s", hashID)
policy.Description = "synthetic policy" sn := NewServiceName(s.ServiceName, entMeta)
policy.Description = fmt.Sprintf("synthetic policy for service identity %q", sn.String())
policy.Rules = rules policy.Rules = rules
policy.Syntax = acl.SyntaxCurrent policy.Syntax = acl.SyntaxCurrent
policy.Datacenters = s.Datacenters policy.Datacenters = s.Datacenters
@ -202,7 +203,7 @@ func (s *ACLNodeIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy {
policy := &ACLPolicy{} policy := &ACLPolicy{}
policy.ID = hashID policy.ID = hashID
policy.Name = fmt.Sprintf("synthetic-policy-%s", hashID) policy.Name = fmt.Sprintf("synthetic-policy-%s", hashID)
policy.Description = "synthetic policy" policy.Description = fmt.Sprintf("synthetic policy for node identity %q", s.NodeName)
policy.Rules = rules policy.Rules = rules
policy.Syntax = acl.SyntaxCurrent policy.Syntax = acl.SyntaxCurrent
policy.Datacenters = []string{s.Datacenter} policy.Datacenters = []string{s.Datacenter}
@ -1217,6 +1218,7 @@ func (r *ACLTokenSetRequest) RequestDatacenter() string {
type ACLTokenGetRequest struct { type ACLTokenGetRequest struct {
TokenID string // id used for the token lookup TokenID string // id used for the token lookup
TokenIDType ACLTokenIDType // The Type of ID used to lookup the token TokenIDType ACLTokenIDType // The Type of ID used to lookup the token
Expanded bool
Datacenter string // The datacenter to perform the request within Datacenter string // The datacenter to perform the request within
EnterpriseMeta EnterpriseMeta
QueryOptions QueryOptions
@ -1313,9 +1315,28 @@ type ACLTokenResponse struct {
Token *ACLToken Token *ACLToken
Redacted bool // whether the token's secret was redacted Redacted bool // whether the token's secret was redacted
SourceDatacenter string SourceDatacenter string
ExpandedTokenInfo
QueryMeta QueryMeta
} }
type ExpandedTokenInfo struct {
ExpandedPolicies []*ACLPolicy
ExpandedRoles []*ACLRole
NamespaceDefaultPolicyIDs []string
NamespaceDefaultRoleIDs []string
AgentACLDefaultPolicy string
AgentACLDownPolicy string
ResolvedByAgent string
}
type ACLTokenExpanded struct {
*ACLToken
ExpandedTokenInfo
}
// ACLTokenBatchResponse returns multiple Tokens associated with the same metadata // ACLTokenBatchResponse returns multiple Tokens associated with the same metadata
type ACLTokenBatchResponse struct { type ACLTokenBatchResponse struct {
Tokens []*ACLToken Tokens []*ACLToken

View File

@ -69,7 +69,6 @@ func TestStructs_ACLServiceIdentity_SyntheticPolicy(t *testing.T) {
expect := &ACLPolicy{ expect := &ACLPolicy{
Syntax: acl.SyntaxCurrent, Syntax: acl.SyntaxCurrent,
Datacenters: test.datacenters, Datacenters: test.datacenters,
Description: "synthetic policy",
Rules: test.expectRules, Rules: test.expectRules,
} }
@ -79,6 +78,7 @@ func TestStructs_ACLServiceIdentity_SyntheticPolicy(t *testing.T) {
// strip irrelevant fields before equality // strip irrelevant fields before equality
got.ID = "" got.ID = ""
got.Name = "" got.Name = ""
got.Description = ""
got.Hash = nil got.Hash = nil
require.Equal(t, expect, got) require.Equal(t, expect, got)
}) })

View File

@ -62,6 +62,20 @@ type ACLToken struct {
AuthMethodNamespace string `json:",omitempty"` AuthMethodNamespace string `json:",omitempty"`
} }
type ACLTokenExpanded struct {
ExpandedPolicies []ACLPolicy
ExpandedRoles []ACLRole
NamespaceDefaultPolicies []string
NamespaceDefaultRoles []string
AgentACLDefaultPolicy string
AgentACLDownPolicy string
ResolvedByAgent string
ACLToken
}
type ACLTokenListEntry struct { type ACLTokenListEntry struct {
CreateIndex uint64 CreateIndex uint64
ModifyIndex uint64 ModifyIndex uint64
@ -788,6 +802,32 @@ func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta,
return &out, qm, nil return &out, qm, nil
} }
// TokenReadExpanded retrieves the full token details, as well as the contents of any policies affecting the token.
// The tokenID parameter must be a valid Accessor ID of an existing token.
func (a *ACL) TokenReadExpanded(tokenID string, q *QueryOptions) (*ACLTokenExpanded, *QueryMeta, error) {
r := a.c.newRequest("GET", "/v1/acl/token/"+tokenID)
r.setQueryOptions(q)
r.params.Set("expanded", "true")
rtt, resp, err := a.c.doRequest(r)
if err != nil {
return nil, nil, err
}
defer closeResponseBody(resp)
if err := requireOK(resp); err != nil {
return nil, nil, err
}
qm := &QueryMeta{}
parseQueryMeta(resp, qm)
qm.RequestTime = rtt
var out ACLTokenExpanded
if err := decodeBody(resp, &out); err != nil {
return nil, nil, err
}
return &out, qm, nil
}
// TokenReadSelf retrieves the full token details of the token currently // TokenReadSelf retrieves the full token details of the token currently
// assigned to the API Client. In this manner its possible to read a token // assigned to the API Client. In this manner its possible to read a token
// by its Secret ID. // by its Secret ID.

View File

@ -6,17 +6,22 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
) )
const ( const (
PrettyFormat string = "pretty" PrettyFormat string = "pretty"
JSONFormat string = "json" JSONFormat string = "json"
WHITESPACE_2 string = "\t"
WHITESPACE_4 string = "\t\t"
) )
// Formatter defines methods provided by token command output formatter // Formatter defines methods provided by token command output formatter
type Formatter interface { type Formatter interface {
FormatToken(token *api.ACLToken) (string, error) FormatToken(token *api.ACLToken) (string, error)
FormatTokenExpanded(token *api.ACLTokenExpanded) (string, error)
FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error) FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error)
} }
@ -108,6 +113,159 @@ func (f *prettyFormatter) FormatToken(token *api.ACLToken) (string, error) {
return buffer.String(), nil 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 := structs.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.NamespaceDefaultPolicies) > 0 || len(token.NamespaceDefaultRoles) > 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.NamespaceDefaultPolicies {
formatPolicy(policies[policyID], WHITESPACE_2)
}
buffer.WriteString("Namespace Role Defaults:\n")
for _, roleID := range token.NamespaceDefaultRoles {
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) { func (f *prettyFormatter) FormatTokenList(tokens []*api.ACLTokenListEntry) (string, error) {
var buffer bytes.Buffer var buffer bytes.Buffer
@ -204,3 +362,11 @@ func (f *jsonFormatter) FormatToken(token *api.ACLToken) (string, error) {
} }
return string(b), nil 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
}

View File

@ -0,0 +1,12 @@
//go:build !consulent
// +build !consulent
package token
import (
"testing"
)
func TestFormatTokenExpanded(t *testing.T) {
testFormatTokenExpanded(t, "FormatTokenExpanded/oss")
}

View File

@ -254,3 +254,239 @@ func TestFormatTokenList(t *testing.T) {
}) })
} }
} }
type testCase struct {
tokenExpanded api.ACLTokenExpanded
overrideGoldenName string
}
func timeRef(in time.Time) *time.Time {
return &in
}
var expandedTokenTestCases = map[string]testCase{
"basic": {
tokenExpanded: api.ACLTokenExpanded{
ExpandedPolicies: []api.ACLPolicy{
{
ID: "beb04680-815b-4d7c-9e33-3d707c24672c",
Name: "foo",
Description: "user policy on token",
Rules: `service_prefix "" {
policy = "read"
}`,
},
{
ID: "18788457-584c-4812-80d3-23d403148a90",
Name: "bar",
Description: "other user policy on token",
Rules: `operator = "read"`,
},
},
AgentACLDefaultPolicy: "allow",
AgentACLDownPolicy: "deny",
ResolvedByAgent: "leader",
ACLToken: api.ACLToken{
AccessorID: "fbd2447f-7479-4329-ad13-b021d74f86ba",
SecretID: "869c6e91-4de9-4dab-b56e-87548435f9c6",
Description: "test token",
Local: false,
CreateTime: time.Date(2020, 5, 22, 18, 52, 31, 0, time.UTC),
Hash: []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'},
CreateIndex: 42,
ModifyIndex: 100,
Policies: []*api.ACLLink{
{
ID: "beb04680-815b-4d7c-9e33-3d707c24672c",
Name: "foo",
},
{
ID: "18788457-584c-4812-80d3-23d403148a90",
Name: "bar",
},
},
},
},
},
"complex": {
tokenExpanded: api.ACLTokenExpanded{
ExpandedPolicies: []api.ACLPolicy{
{
ID: "beb04680-815b-4d7c-9e33-3d707c24672c",
Name: "hobbiton",
Description: "user policy on token",
Rules: `service_prefix "" {
policy = "read"
}`,
},
{
ID: "18788457-584c-4812-80d3-23d403148a90",
Name: "bywater",
Description: "other user policy on token",
Rules: `operator = "read"`,
},
{
ID: "6204f4cd-4709-441c-ac1b-cb029e940263",
Name: "shire-policy",
Description: "policy for shire role",
Rules: `operator = "write"`,
},
{
ID: "e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93",
Name: "west-farthing-policy",
Description: "policy for west-farthing role",
Rules: `service "foo" {
policy = "read"
}`,
},
{
ID: "2b582ff1-4a43-457f-8a2b-30a8265e29a5",
Name: "default-policy-1",
Description: "default policy 1",
Rules: `key "foo" { policy = "write" }`,
},
{
ID: "b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea",
Name: "default-policy-2",
Description: "default policy 2",
Rules: `key "bar" { policy = "read" }`,
},
},
ExpandedRoles: []api.ACLRole{
{
ID: "3b0a78fe-b9c3-40de-b8ea-7d4d6674b366",
Name: "shire",
Description: "shire role",
Policies: []*api.ACLRolePolicyLink{
{
ID: "6204f4cd-4709-441c-ac1b-cb029e940263",
},
},
ServiceIdentities: []*api.ACLServiceIdentity{
{
ServiceName: "foo",
Datacenters: []string{"middleearth-southwest"},
},
},
},
{
ID: "6c9d1e1d-34bc-4d55-80f3-add0890ad791",
Name: "west-farthing",
Description: "west-farthing role",
Policies: []*api.ACLRolePolicyLink{
{
ID: "e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93",
},
},
NodeIdentities: []*api.ACLNodeIdentity{
{
NodeName: "bar",
Datacenter: "middleearth-southwest",
},
},
},
{
ID: "56033f2b-e1a6-4905-b71d-e011c862bc65",
Name: "ns-default",
Description: "default role",
Policies: []*api.ACLRolePolicyLink{
{
ID: "b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea",
},
},
ServiceIdentities: []*api.ACLServiceIdentity{
{
ServiceName: "web",
Datacenters: []string{"middleearth-northeast"},
},
},
NodeIdentities: []*api.ACLNodeIdentity{
{
NodeName: "db",
Datacenter: "middleearth-northwest",
},
},
},
},
NamespaceDefaultPolicies: []string{"2b582ff1-4a43-457f-8a2b-30a8265e29a5"},
NamespaceDefaultRoles: []string{"56033f2b-e1a6-4905-b71d-e011c862bc65"},
AgentACLDefaultPolicy: "deny",
AgentACLDownPolicy: "extend-cache",
ResolvedByAgent: "server-1",
ACLToken: api.ACLToken{
AccessorID: "fbd2447f-7479-4329-ad13-b021d74f86ba",
SecretID: "869c6e91-4de9-4dab-b56e-87548435f9c6",
Namespace: "foo",
Description: "test token",
Local: false,
AuthMethod: "bar",
AuthMethodNamespace: "baz",
CreateTime: time.Date(2020, 5, 22, 18, 52, 31, 0, time.UTC),
ExpirationTime: timeRef(time.Date(2020, 5, 22, 19, 52, 31, 0, time.UTC)),
Hash: []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'},
CreateIndex: 5,
ModifyIndex: 10,
Policies: []*api.ACLLink{
{
ID: "beb04680-815b-4d7c-9e33-3d707c24672c",
Name: "hobbiton",
},
{
ID: "18788457-584c-4812-80d3-23d403148a90",
Name: "bywater",
},
},
Roles: []*api.ACLLink{
{
ID: "3b0a78fe-b9c3-40de-b8ea-7d4d6674b366",
Name: "shire",
},
{
ID: "6c9d1e1d-34bc-4d55-80f3-add0890ad791",
Name: "west-farthing",
},
},
ServiceIdentities: []*api.ACLServiceIdentity{
{
ServiceName: "gardener",
Datacenters: []string{"middleearth-northwest"},
},
},
NodeIdentities: []*api.ACLNodeIdentity{
{
NodeName: "bagend",
Datacenter: "middleearth-northwest",
},
},
},
},
},
}
func testFormatTokenExpanded(t *testing.T, dirPath string) {
formatters := map[string]Formatter{
"pretty": newPrettyFormatter(false),
"pretty-meta": newPrettyFormatter(true),
// the JSON formatter ignores the showMeta
"json": newJSONFormatter(false),
}
for name, tcase := range expandedTokenTestCases {
t.Run(name, func(t *testing.T) {
for fmtName, formatter := range formatters {
t.Run(fmtName, func(t *testing.T) {
actual, err := formatter.FormatTokenExpanded(&tcase.tokenExpanded)
require.NoError(t, err)
gName := fmt.Sprintf("%s.%s", name, fmtName)
if tcase.overrideGoldenName != "" {
gName = tcase.overrideGoldenName
}
expected := golden(t, path.Join(dirPath, gName), actual)
require.Equal(t, expected, actual)
})
}
})
}
}

View File

@ -28,6 +28,7 @@ type cmd struct {
self bool self bool
showMeta bool showMeta bool
format string format string
expanded bool
} }
func (c *cmd) init() { func (c *cmd) init() {
@ -36,6 +37,8 @@ func (c *cmd) init() {
"as the content hash and Raft indices should be shown for each entry") "as the content hash and Raft indices should be shown for each entry")
c.flags.BoolVar(&c.self, "self", false, "Indicates that the current HTTP token "+ c.flags.BoolVar(&c.self, "self", false, "Indicates that the current HTTP token "+
"should be read by secret ID instead of expecting a -id option") "should be read by secret ID instead of expecting a -id option")
c.flags.BoolVar(&c.expanded, "expanded", false, "Indicates that the contents of the "+
" policies and roles affecting the token should also be shown.")
c.flags.StringVar(&c.tokenID, "id", "", "The Accessor ID of the token to read. "+ c.flags.StringVar(&c.tokenID, "id", "", "The Accessor ID of the token to read. "+
"It may be specified as a unique ID prefix but will error if the prefix "+ "It may be specified as a unique ID prefix but will error if the prefix "+
"matches multiple token Accessor IDs") "matches multiple token Accessor IDs")
@ -69,6 +72,7 @@ func (c *cmd) Run(args []string) int {
} }
var t *api.ACLToken var t *api.ACLToken
var expanded *api.ACLTokenExpanded
if !c.self { if !c.self {
tokenID, err := acl.GetTokenIDFromPartial(client, c.tokenID) tokenID, err := acl.GetTokenIDFromPartial(client, c.tokenID)
if err != nil { if err != nil {
@ -76,7 +80,12 @@ func (c *cmd) Run(args []string) int {
return 1 return 1
} }
if !c.expanded {
t, _, err = client.ACL().TokenRead(tokenID, nil) t, _, err = client.ACL().TokenRead(tokenID, nil)
} else {
expanded, _, err = client.ACL().TokenReadExpanded(tokenID, nil)
}
if err != nil { if err != nil {
c.UI.Error(fmt.Sprintf("Error reading token %q: %v", tokenID, err)) c.UI.Error(fmt.Sprintf("Error reading token %q: %v", tokenID, err))
return 1 return 1
@ -94,7 +103,12 @@ func (c *cmd) Run(args []string) int {
c.UI.Error(err.Error()) c.UI.Error(err.Error())
return 1 return 1
} }
out, err := formatter.FormatToken(t) var out string
if !c.expanded {
out, err = formatter.FormatToken(t)
} else {
out, err = formatter.FormatTokenExpanded(expanded)
}
if err != nil { if err != nil {
c.UI.Error(err.Error()) c.UI.Error(err.Error())
return 1 return 1

View File

@ -0,0 +1,48 @@
{
"ExpandedPolicies": [
{
"ID": "beb04680-815b-4d7c-9e33-3d707c24672c",
"Name": "foo",
"Description": "user policy on token",
"Rules": "service_prefix \"\" {\n policy = \"read\"\n}",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "18788457-584c-4812-80d3-23d403148a90",
"Name": "bar",
"Description": "other user policy on token",
"Rules": "operator = \"read\"",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
}
],
"ExpandedRoles": null,
"NamespaceDefaultPolicies": null,
"NamespaceDefaultRoles": null,
"AgentACLDefaultPolicy": "allow",
"AgentACLDownPolicy": "deny",
"ResolvedByAgent": "leader",
"CreateIndex": 42,
"ModifyIndex": 100,
"AccessorID": "fbd2447f-7479-4329-ad13-b021d74f86ba",
"SecretID": "869c6e91-4de9-4dab-b56e-87548435f9c6",
"Description": "test token",
"Policies": [
{
"ID": "beb04680-815b-4d7c-9e33-3d707c24672c",
"Name": "foo"
},
{
"ID": "18788457-584c-4812-80d3-23d403148a90",
"Name": "bar"
}
],
"Local": false,
"CreateTime": "2020-05-22T18:52:31Z",
"Hash": "YWJjZGVmZ2g="
}

View File

@ -0,0 +1,34 @@
AccessorID: fbd2447f-7479-4329-ad13-b021d74f86ba
SecretID: 869c6e91-4de9-4dab-b56e-87548435f9c6
Description: test token
Local: false
Create Time: 2020-05-22 18:52:31 +0000 UTC
Hash: 6162636465666768
Create Index: 42
Modify Index: 100
Policies:
Policy Name: foo
ID: beb04680-815b-4d7c-9e33-3d707c24672c
Description: user policy on token
Rules:
service_prefix "" {
policy = "read"
}
Policy Name: bar
ID: 18788457-584c-4812-80d3-23d403148a90
Description: other user policy on token
Rules:
operator = "read"
=== End of Authorizer Layer 0: Token ===
=== Start of Authorizer Layer 2: Agent Configuration Defaults (Inherited) ===
Description: Defined at request-time by the agent that resolves the ACL token; other agents may have different configuration defaults
Resolved By Agent: "leader"
Default Policy: allow
Description: Backstop rule used if no preceding layer has a matching rule (refer to default_policy option in agent configuration)
Down Policy: deny
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)

View File

@ -0,0 +1,31 @@
AccessorID: fbd2447f-7479-4329-ad13-b021d74f86ba
SecretID: 869c6e91-4de9-4dab-b56e-87548435f9c6
Description: test token
Local: false
Create Time: 2020-05-22 18:52:31 +0000 UTC
Policies:
Policy Name: foo
ID: beb04680-815b-4d7c-9e33-3d707c24672c
Description: user policy on token
Rules:
service_prefix "" {
policy = "read"
}
Policy Name: bar
ID: 18788457-584c-4812-80d3-23d403148a90
Description: other user policy on token
Rules:
operator = "read"
=== End of Authorizer Layer 0: Token ===
=== Start of Authorizer Layer 2: Agent Configuration Defaults (Inherited) ===
Description: Defined at request-time by the agent that resolves the ACL token; other agents may have different configuration defaults
Resolved By Agent: "leader"
Default Policy: allow
Description: Backstop rule used if no preceding layer has a matching rule (refer to default_policy option in agent configuration)
Down Policy: deny
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)

View File

@ -0,0 +1,191 @@
{
"ExpandedPolicies": [
{
"ID": "beb04680-815b-4d7c-9e33-3d707c24672c",
"Name": "hobbiton",
"Description": "user policy on token",
"Rules": "service_prefix \"\" {\n policy = \"read\"\n}",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "18788457-584c-4812-80d3-23d403148a90",
"Name": "bywater",
"Description": "other user policy on token",
"Rules": "operator = \"read\"",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "6204f4cd-4709-441c-ac1b-cb029e940263",
"Name": "shire-policy",
"Description": "policy for shire role",
"Rules": "operator = \"write\"",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93",
"Name": "west-farthing-policy",
"Description": "policy for west-farthing role",
"Rules": "service \"foo\" {\n policy = \"read\"\n}",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "2b582ff1-4a43-457f-8a2b-30a8265e29a5",
"Name": "default-policy-1",
"Description": "default policy 1",
"Rules": "key \"foo\" { policy = \"write\" }",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea",
"Name": "default-policy-2",
"Description": "default policy 2",
"Rules": "key \"bar\" { policy = \"read\" }",
"Datacenters": null,
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
}
],
"ExpandedRoles": [
{
"ID": "3b0a78fe-b9c3-40de-b8ea-7d4d6674b366",
"Name": "shire",
"Description": "shire role",
"Policies": [
{
"ID": "6204f4cd-4709-441c-ac1b-cb029e940263",
"Name": ""
}
],
"ServiceIdentities": [
{
"ServiceName": "foo",
"Datacenters": [
"middleearth-southwest"
]
}
],
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "6c9d1e1d-34bc-4d55-80f3-add0890ad791",
"Name": "west-farthing",
"Description": "west-farthing role",
"Policies": [
{
"ID": "e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93",
"Name": ""
}
],
"NodeIdentities": [
{
"NodeName": "bar",
"Datacenter": "middleearth-southwest"
}
],
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
},
{
"ID": "56033f2b-e1a6-4905-b71d-e011c862bc65",
"Name": "ns-default",
"Description": "default role",
"Policies": [
{
"ID": "b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea",
"Name": ""
}
],
"ServiceIdentities": [
{
"ServiceName": "web",
"Datacenters": [
"middleearth-northeast"
]
}
],
"NodeIdentities": [
{
"NodeName": "db",
"Datacenter": "middleearth-northwest"
}
],
"Hash": null,
"CreateIndex": 0,
"ModifyIndex": 0
}
],
"NamespaceDefaultPolicies": [
"2b582ff1-4a43-457f-8a2b-30a8265e29a5"
],
"NamespaceDefaultRoles": [
"56033f2b-e1a6-4905-b71d-e011c862bc65"
],
"AgentACLDefaultPolicy": "deny",
"AgentACLDownPolicy": "extend-cache",
"ResolvedByAgent": "server-1",
"CreateIndex": 5,
"ModifyIndex": 10,
"AccessorID": "fbd2447f-7479-4329-ad13-b021d74f86ba",
"SecretID": "869c6e91-4de9-4dab-b56e-87548435f9c6",
"Description": "test token",
"Policies": [
{
"ID": "beb04680-815b-4d7c-9e33-3d707c24672c",
"Name": "hobbiton"
},
{
"ID": "18788457-584c-4812-80d3-23d403148a90",
"Name": "bywater"
}
],
"Roles": [
{
"ID": "3b0a78fe-b9c3-40de-b8ea-7d4d6674b366",
"Name": "shire"
},
{
"ID": "6c9d1e1d-34bc-4d55-80f3-add0890ad791",
"Name": "west-farthing"
}
],
"ServiceIdentities": [
{
"ServiceName": "gardener",
"Datacenters": [
"middleearth-northwest"
]
}
],
"NodeIdentities": [
{
"NodeName": "bagend",
"Datacenter": "middleearth-northwest"
}
],
"Local": false,
"AuthMethod": "bar",
"ExpirationTime": "2020-05-22T19:52:31Z",
"CreateTime": "2020-05-22T18:52:31Z",
"Hash": "YWJjZGVmZ2g=",
"Namespace": "foo",
"AuthMethodNamespace": "baz"
}

View File

@ -0,0 +1,166 @@
AccessorID: fbd2447f-7479-4329-ad13-b021d74f86ba
SecretID: 869c6e91-4de9-4dab-b56e-87548435f9c6
Namespace: foo
Description: test token
Local: false
Auth Method: bar (Namespace: baz)
Create Time: 2020-05-22 18:52:31 +0000 UTC
Expiration Time: 2020-05-22 19:52:31 +0000 UTC
Hash: 6162636465666768
Create Index: 5
Modify Index: 10
Policies:
Policy Name: hobbiton
ID: beb04680-815b-4d7c-9e33-3d707c24672c
Description: user policy on token
Rules:
service_prefix "" {
policy = "read"
}
Policy Name: bywater
ID: 18788457-584c-4812-80d3-23d403148a90
Description: other user policy on token
Rules:
operator = "read"
Service Identities:
Name: gardener (Datacenters: middleearth-northwest)
Description: synthetic policy for service identity "gardener"
Rules:
service "gardener" {
policy = "write"
}
service "gardener-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Node Identities:
Name: bagend (Datacenter: middleearth-northwest)
Description: synthetic policy for node identity "bagend"
Rules:
node "bagend" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
Roles:
Role Name: shire
ID: 3b0a78fe-b9c3-40de-b8ea-7d4d6674b366
Description: shire role
Policies:
Policy Name: shire-policy
ID: 6204f4cd-4709-441c-ac1b-cb029e940263
Description: policy for shire role
Rules:
operator = "write"
Service Identities:
Name: foo (Datacenters: middleearth-southwest)
Description: synthetic policy for service identity "foo"
Rules:
service "foo" {
policy = "write"
}
service "foo-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Role Name: west-farthing
ID: 6c9d1e1d-34bc-4d55-80f3-add0890ad791
Description: west-farthing role
Policies:
Policy Name: west-farthing-policy
ID: e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93
Description: policy for west-farthing role
Rules:
service "foo" {
policy = "read"
}
Node Identities:
Name: bar (Datacenter: middleearth-southwest)
Description: synthetic policy for node identity "bar"
Rules:
node "bar" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
=== End of Authorizer Layer 0: Token ===
=== Start of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===
Description: ACL Roles inherited by all Tokens in Namespace "foo"
Namespace Policy Defaults:
Policy Name: default-policy-1
ID: 2b582ff1-4a43-457f-8a2b-30a8265e29a5
Description: default policy 1
Rules:
key "foo" { policy = "write" }
Namespace Role Defaults:
Role Name: ns-default
ID: 56033f2b-e1a6-4905-b71d-e011c862bc65
Description: default role
Policies:
Policy Name: default-policy-2
ID: b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea
Description: default policy 2
Rules:
key "bar" { policy = "read" }
Service Identities:
Name: web (Datacenters: middleearth-northeast)
Description: synthetic policy for service identity "web"
Rules:
service "web" {
policy = "write"
}
service "web-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Node Identities:
Name: db (Datacenter: middleearth-northwest)
Description: synthetic policy for node identity "db"
Rules:
node "db" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
=== End of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===
=== Start of Authorizer Layer 2: Agent Configuration Defaults (Inherited) ===
Description: Defined at request-time by the agent that resolves the ACL token; other agents may have different configuration defaults
Resolved By Agent: "server-1"
Default Policy: deny
Description: Backstop rule used if no preceding layer has a matching rule (refer to default_policy option in agent configuration)
Down Policy: extend-cache
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)

View File

@ -0,0 +1,163 @@
AccessorID: fbd2447f-7479-4329-ad13-b021d74f86ba
SecretID: 869c6e91-4de9-4dab-b56e-87548435f9c6
Namespace: foo
Description: test token
Local: false
Auth Method: bar (Namespace: baz)
Create Time: 2020-05-22 18:52:31 +0000 UTC
Expiration Time: 2020-05-22 19:52:31 +0000 UTC
Policies:
Policy Name: hobbiton
ID: beb04680-815b-4d7c-9e33-3d707c24672c
Description: user policy on token
Rules:
service_prefix "" {
policy = "read"
}
Policy Name: bywater
ID: 18788457-584c-4812-80d3-23d403148a90
Description: other user policy on token
Rules:
operator = "read"
Service Identities:
Name: gardener (Datacenters: middleearth-northwest)
Description: synthetic policy for service identity "gardener"
Rules:
service "gardener" {
policy = "write"
}
service "gardener-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Node Identities:
Name: bagend (Datacenter: middleearth-northwest)
Description: synthetic policy for node identity "bagend"
Rules:
node "bagend" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
Roles:
Role Name: shire
ID: 3b0a78fe-b9c3-40de-b8ea-7d4d6674b366
Description: shire role
Policies:
Policy Name: shire-policy
ID: 6204f4cd-4709-441c-ac1b-cb029e940263
Description: policy for shire role
Rules:
operator = "write"
Service Identities:
Name: foo (Datacenters: middleearth-southwest)
Description: synthetic policy for service identity "foo"
Rules:
service "foo" {
policy = "write"
}
service "foo-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Role Name: west-farthing
ID: 6c9d1e1d-34bc-4d55-80f3-add0890ad791
Description: west-farthing role
Policies:
Policy Name: west-farthing-policy
ID: e86f0d1f-71b1-4690-bdfd-ff8c2cd4ae93
Description: policy for west-farthing role
Rules:
service "foo" {
policy = "read"
}
Node Identities:
Name: bar (Datacenter: middleearth-southwest)
Description: synthetic policy for node identity "bar"
Rules:
node "bar" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
=== End of Authorizer Layer 0: Token ===
=== Start of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===
Description: ACL Roles inherited by all Tokens in Namespace "foo"
Namespace Policy Defaults:
Policy Name: default-policy-1
ID: 2b582ff1-4a43-457f-8a2b-30a8265e29a5
Description: default policy 1
Rules:
key "foo" { policy = "write" }
Namespace Role Defaults:
Role Name: ns-default
ID: 56033f2b-e1a6-4905-b71d-e011c862bc65
Description: default role
Policies:
Policy Name: default-policy-2
ID: b55dce64-f2cc-4eb5-8e5f-50e90e63c6ea
Description: default policy 2
Rules:
key "bar" { policy = "read" }
Service Identities:
Name: web (Datacenters: middleearth-northeast)
Description: synthetic policy for service identity "web"
Rules:
service "web" {
policy = "write"
}
service "web-sidecar-proxy" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
node_prefix "" {
policy = "read"
}
Node Identities:
Name: db (Datacenter: middleearth-northwest)
Description: synthetic policy for node identity "db"
Rules:
node "db" {
policy = "write"
}
service_prefix "" {
policy = "read"
}
=== End of Authorizer Layer 1: Token Namespaces Defaults (Inherited) ===
=== Start of Authorizer Layer 2: Agent Configuration Defaults (Inherited) ===
Description: Defined at request-time by the agent that resolves the ACL token; other agents may have different configuration defaults
Resolved By Agent: "server-1"
Default Policy: deny
Description: Backstop rule used if no preceding layer has a matching rule (refer to default_policy option in agent configuration)
Down Policy: extend-cache
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)