Add expanded token read flag and endpoint option
This commit is contained in:
parent
aae6d8080d
commit
9a2474381a
|
@ -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.
|
||||
```
|
|
@ -378,6 +378,9 @@ func (s *HTTPHandlers) ACLTokenGet(resp http.ResponseWriter, req *http.Request,
|
|||
if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := req.URL.Query()["expanded"]; ok {
|
||||
args.Expanded = true
|
||||
}
|
||||
|
||||
if args.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
|
||||
}
|
||||
|
||||
if args.Expanded {
|
||||
expanded := &structs.ACLTokenExpanded{
|
||||
ACLToken: out.Token,
|
||||
ExpandedTokenInfo: out.ExpandedTokenInfo,
|
||||
}
|
||||
return expanded, nil
|
||||
}
|
||||
|
||||
return out.Token, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -724,6 +724,17 @@ func TestACL_HTTP(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
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) {
|
||||
expected := tokenMap[idMap["token-test"]]
|
||||
req, _ := http.NewRequest("GET", "/v1/acl/token/self?token="+expected.SecretID, nil)
|
||||
|
|
|
@ -325,10 +325,99 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke
|
|||
if token == nil {
|
||||
return errNotFound
|
||||
}
|
||||
|
||||
if args.Expanded {
|
||||
info, err := a.lookupExpandedTokenInfo(ws, state, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reply.ExpandedTokenInfo = info
|
||||
}
|
||||
|
||||
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 {
|
||||
if err := a.aclPreCheck(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -5,7 +5,9 @@ package consul
|
|||
|
||||
import (
|
||||
"github.com/hashicorp/consul/agent/consul/authmethod"
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
)
|
||||
|
||||
func (a *ACL) tokenUpsertValidateEnterprise(token *structs.ACLToken, existing *structs.ACLToken) error {
|
||||
|
@ -37,3 +39,7 @@ func computeTargetEnterpriseMeta(
|
|||
) (*structs.EnterpriseMeta, error) {
|
||||
return &structs.EnterpriseMeta{}, nil
|
||||
}
|
||||
|
||||
func getTokenNamespaceDefaults(ws memdb.WatchSet, state *state.Store, entMeta *structs.EnterpriseMeta) ([]string, []string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
|
|
@ -204,6 +204,181 @@ func TestACLEndpoint_TokenRead(t *testing.T) {
|
|||
require.Nil(t, resp.Token)
|
||||
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) {
|
||||
|
|
|
@ -158,7 +158,8 @@ func (s *ACLServiceIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy
|
|||
policy := &ACLPolicy{}
|
||||
policy.ID = 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.Syntax = acl.SyntaxCurrent
|
||||
policy.Datacenters = s.Datacenters
|
||||
|
@ -202,7 +203,7 @@ func (s *ACLNodeIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy {
|
|||
policy := &ACLPolicy{}
|
||||
policy.ID = 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.Syntax = acl.SyntaxCurrent
|
||||
policy.Datacenters = []string{s.Datacenter}
|
||||
|
@ -1217,7 +1218,8 @@ func (r *ACLTokenSetRequest) RequestDatacenter() string {
|
|||
type ACLTokenGetRequest struct {
|
||||
TokenID string // id used for the token lookup
|
||||
TokenIDType ACLTokenIDType // The Type of ID used to lookup the token
|
||||
Datacenter string // The datacenter to perform the request within
|
||||
Expanded bool
|
||||
Datacenter string // The datacenter to perform the request within
|
||||
EnterpriseMeta
|
||||
QueryOptions
|
||||
}
|
||||
|
@ -1313,9 +1315,28 @@ type ACLTokenResponse struct {
|
|||
Token *ACLToken
|
||||
Redacted bool // whether the token's secret was redacted
|
||||
SourceDatacenter string
|
||||
|
||||
ExpandedTokenInfo
|
||||
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
|
||||
type ACLTokenBatchResponse struct {
|
||||
Tokens []*ACLToken
|
||||
|
|
|
@ -69,7 +69,6 @@ func TestStructs_ACLServiceIdentity_SyntheticPolicy(t *testing.T) {
|
|||
expect := &ACLPolicy{
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
Datacenters: test.datacenters,
|
||||
Description: "synthetic policy",
|
||||
Rules: test.expectRules,
|
||||
}
|
||||
|
||||
|
@ -79,6 +78,7 @@ func TestStructs_ACLServiceIdentity_SyntheticPolicy(t *testing.T) {
|
|||
// strip irrelevant fields before equality
|
||||
got.ID = ""
|
||||
got.Name = ""
|
||||
got.Description = ""
|
||||
got.Hash = nil
|
||||
require.Equal(t, expect, got)
|
||||
})
|
||||
|
|
40
api/acl.go
40
api/acl.go
|
@ -62,6 +62,20 @@ type ACLToken struct {
|
|||
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 {
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
|
@ -788,6 +802,32 @@ func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta,
|
|||
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
|
||||
// assigned to the API Client. In this manner its possible to read a token
|
||||
// by its Secret ID.
|
||||
|
|
|
@ -6,17 +6,22 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"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)
|
||||
}
|
||||
|
||||
|
@ -108,6 +113,159 @@ func (f *prettyFormatter) FormatToken(token *api.ACLToken) (string, error) {
|
|||
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 Namespace’s 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 Namespace’s 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
|
||||
|
||||
|
@ -204,3 +362,11 @@ func (f *jsonFormatter) FormatToken(token *api.ACLToken) (string, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
//go:build !consulent
|
||||
// +build !consulent
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatTokenExpanded(t *testing.T) {
|
||||
testFormatTokenExpanded(t, "FormatTokenExpanded/oss")
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ type cmd struct {
|
|||
self bool
|
||||
showMeta bool
|
||||
format string
|
||||
expanded bool
|
||||
}
|
||||
|
||||
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")
|
||||
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")
|
||||
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. "+
|
||||
"It may be specified as a unique ID prefix but will error if the prefix "+
|
||||
"matches multiple token Accessor IDs")
|
||||
|
@ -69,6 +72,7 @@ func (c *cmd) Run(args []string) int {
|
|||
}
|
||||
|
||||
var t *api.ACLToken
|
||||
var expanded *api.ACLTokenExpanded
|
||||
if !c.self {
|
||||
tokenID, err := acl.GetTokenIDFromPartial(client, c.tokenID)
|
||||
if err != nil {
|
||||
|
@ -76,7 +80,12 @@ func (c *cmd) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
t, _, err = client.ACL().TokenRead(tokenID, nil)
|
||||
if !c.expanded {
|
||||
t, _, err = client.ACL().TokenRead(tokenID, nil)
|
||||
} else {
|
||||
expanded, _, err = client.ACL().TokenReadExpanded(tokenID, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error reading token %q: %v", tokenID, err))
|
||||
return 1
|
||||
|
@ -94,7 +103,12 @@ func (c *cmd) Run(args []string) int {
|
|||
c.UI.Error(err.Error())
|
||||
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 {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
|
|
|
@ -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="
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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"
|
||||
}
|
166
command/acl/token/testdata/FormatTokenExpanded/oss/complex.pretty-meta.golden
vendored
Normal file
166
command/acl/token/testdata/FormatTokenExpanded/oss/complex.pretty-meta.golden
vendored
Normal 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 Namespace’s 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 Namespace’s 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)
|
||||
|
|
@ -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 Namespace’s 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 Namespace’s 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)
|
||||
|
Loading…
Reference in New Issue