mirror of https://github.com/status-im/consul.git
Add support for querying tokens by service name. (#18667)
Add support for querying tokens by service name The consul-k8s endpoints controller has a workflow where it fetches all tokens. This is not performant for large clusters, where there may be a sizable number of tokens. This commit attempts to alleviate that problem and introduces a new way to query by the token's service name.
This commit is contained in:
parent
ec507fe4a8
commit
56917eb4c9
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
api: Add support for listing ACL tokens by service name.
|
||||||
|
```
|
|
@ -294,6 +294,7 @@ func (s *HTTPHandlers) ACLTokenList(resp http.ResponseWriter, req *http.Request)
|
||||||
args.Policy = req.URL.Query().Get("policy")
|
args.Policy = req.URL.Query().Get("policy")
|
||||||
args.Role = req.URL.Query().Get("role")
|
args.Role = req.URL.Query().Get("role")
|
||||||
args.AuthMethod = req.URL.Query().Get("authmethod")
|
args.AuthMethod = req.URL.Query().Get("authmethod")
|
||||||
|
args.ServiceName = req.URL.Query().Get("servicename")
|
||||||
if err := parseACLAuthMethodEnterpriseMeta(req, &args.ACLAuthMethodEnterpriseMeta); err != nil {
|
if err := parseACLAuthMethodEnterpriseMeta(req, &args.ACLAuthMethodEnterpriseMeta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1328,6 +1328,38 @@ func TestACL_HTTP(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
testutil.RequireErrorContains(t, err, "Only lowercase alphanumeric")
|
testutil.RequireErrorContains(t, err, "Only lowercase alphanumeric")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Create with valid service identity", func(t *testing.T) {
|
||||||
|
tokenInput := &structs.ACLToken{
|
||||||
|
Description: "token for service identity sn1",
|
||||||
|
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||||
|
{
|
||||||
|
ServiceName: "sn1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", "/v1/acl/token", jsonBody(tokenInput))
|
||||||
|
req.Header.Add("X-Consul-Token", "root")
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
_, err := a.srv.ACLTokenCreate(resp, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("List by ServiceName", func(t *testing.T) {
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/acl/tokens?servicename=sn1", nil)
|
||||||
|
req.Header.Add("X-Consul-Token", "root")
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
raw, err := a.srv.ACLTokenList(resp, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
tokens, ok := raw.(structs.ACLTokenListStubs)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Len(t, tokens, 1)
|
||||||
|
token := tokens[0]
|
||||||
|
require.Equal(t, "token for service identity sn1", token.Description)
|
||||||
|
require.Len(t, token.ServiceIdentities, 1)
|
||||||
|
require.Equal(t, "sn1", token.ServiceIdentities[0].ServiceName)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -680,8 +680,18 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
|
return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
|
||||||
func(ws memdb.WatchSet, state *state.Store) error {
|
func(ws memdb.WatchSet, s *state.Store) error {
|
||||||
index, tokens, err := state.ACLTokenList(ws, args.IncludeLocal, args.IncludeGlobal, args.Policy, args.Role, args.AuthMethod, methodMeta, &args.EnterpriseMeta)
|
index, tokens, err := s.ACLTokenListWithParameters(ws, state.ACLTokenListParameters{
|
||||||
|
Local: args.IncludeLocal,
|
||||||
|
Global: args.IncludeGlobal,
|
||||||
|
Policy: args.Policy,
|
||||||
|
Role: args.Role,
|
||||||
|
MethodName: args.AuthMethod,
|
||||||
|
ServiceName: args.ServiceName,
|
||||||
|
MethodMeta: methodMeta,
|
||||||
|
EnterpriseMeta: &args.EnterpriseMeta,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,8 +378,10 @@ func TestACLReplication_Tokens(t *testing.T) {
|
||||||
|
|
||||||
checkSame := func(t *retry.R) {
|
checkSame := func(t *retry.R) {
|
||||||
// only account for global tokens - local tokens shouldn't be replicated
|
// only account for global tokens - local tokens shouldn't be replicated
|
||||||
|
// nolint:staticcheck
|
||||||
index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
// nolint:staticcheck
|
||||||
_, local, err := s2.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
_, local, err := s2.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -483,6 +485,7 @@ func TestACLReplication_Tokens(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// verify dc2 local tokens didn't get blown away
|
// verify dc2 local tokens didn't get blown away
|
||||||
|
// nolint:staticcheck
|
||||||
_, local, err := s2.fsm.State().ACLTokenList(nil, true, false, "", "", "", nil, nil)
|
_, local, err := s2.fsm.State().ACLTokenList(nil, true, false, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, local, 50)
|
require.Len(t, local, 50)
|
||||||
|
@ -821,9 +824,11 @@ func TestACLReplication_AllTypes(t *testing.T) {
|
||||||
|
|
||||||
checkSameTokens := func(t *retry.R) {
|
checkSameTokens := func(t *retry.R) {
|
||||||
// only account for global tokens - local tokens shouldn't be replicated
|
// only account for global tokens - local tokens shouldn't be replicated
|
||||||
|
// nolint:staticcheck
|
||||||
index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Query for all of them, so that we can prove that no globals snuck in.
|
// Query for all of them, so that we can prove that no globals snuck in.
|
||||||
|
// nolint:staticcheck
|
||||||
_, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ func (r *aclTokenReplicator) FetchRemote(srv *Server, lastRemoteIndex uint64) (i
|
||||||
func (r *aclTokenReplicator) FetchLocal(srv *Server) (int, uint64, error) {
|
func (r *aclTokenReplicator) FetchLocal(srv *Server) (int, uint64, error) {
|
||||||
r.local = nil
|
r.local = nil
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
idx, local, err := srv.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, srv.replicationEnterpriseMeta())
|
idx, local, err := srv.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil, srv.replicationEnterpriseMeta())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
|
|
|
@ -623,8 +623,35 @@ func aclTokenGetTxn(tx ReadTxn, ws memdb.WatchSet, value, index string, entMeta
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ACLTokenListParameters struct {
|
||||||
|
Local bool
|
||||||
|
Global bool
|
||||||
|
Policy string
|
||||||
|
Role string
|
||||||
|
ServiceName string
|
||||||
|
MethodName string
|
||||||
|
MethodMeta *acl.EnterpriseMeta
|
||||||
|
EnterpriseMeta *acl.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
// ACLTokenList return a list of ACL Tokens that match the policy, role, and method.
|
// ACLTokenList return a list of ACL Tokens that match the policy, role, and method.
|
||||||
|
// This function should be treated as deprecated, and ACLTokenListWithParameters should be preferred.
|
||||||
|
//
|
||||||
|
// Deprecated: use ACLTokenListWithParameters
|
||||||
func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role, methodName string, methodMeta, entMeta *acl.EnterpriseMeta) (uint64, structs.ACLTokens, error) {
|
func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role, methodName string, methodMeta, entMeta *acl.EnterpriseMeta) (uint64, structs.ACLTokens, error) {
|
||||||
|
return s.ACLTokenListWithParameters(ws, ACLTokenListParameters{
|
||||||
|
Local: local,
|
||||||
|
Global: global,
|
||||||
|
Policy: policy,
|
||||||
|
Role: role,
|
||||||
|
MethodName: methodName,
|
||||||
|
MethodMeta: methodMeta,
|
||||||
|
EnterpriseMeta: entMeta,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLTokenListWithParameters returns a list of ACL Tokens that match the provided parameters.
|
||||||
|
func (s *Store) ACLTokenListWithParameters(ws memdb.WatchSet, params ACLTokenListParameters) (uint64, structs.ACLTokens, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
|
@ -637,43 +664,51 @@ func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role
|
||||||
|
|
||||||
needLocalityFilter := false
|
needLocalityFilter := false
|
||||||
|
|
||||||
if policy == "" && role == "" && methodName == "" {
|
if params.Policy == "" && params.Role == "" && params.MethodName == "" && params.ServiceName == "" {
|
||||||
if global == local {
|
if params.Global == params.Local {
|
||||||
iter, err = aclTokenListAll(tx, entMeta)
|
iter, err = aclTokenListAll(tx, params.EnterpriseMeta)
|
||||||
} else {
|
} else {
|
||||||
iter, err = aclTokenList(tx, entMeta, local)
|
iter, err = aclTokenList(tx, params.EnterpriseMeta, params.Local)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if policy != "" && role == "" && methodName == "" {
|
} else if params.Policy != "" && params.Role == "" && params.MethodName == "" && params.ServiceName == "" {
|
||||||
iter, err = aclTokenListByPolicy(tx, policy, entMeta)
|
// Find by policy
|
||||||
|
iter, err = aclTokenListByPolicy(tx, params.Policy, params.EnterpriseMeta)
|
||||||
needLocalityFilter = true
|
needLocalityFilter = true
|
||||||
|
|
||||||
} else if policy == "" && role != "" && methodName == "" {
|
} else if params.Policy == "" && params.Role != "" && params.MethodName == "" && params.ServiceName == "" {
|
||||||
iter, err = aclTokenListByRole(tx, role, entMeta)
|
// Find by role
|
||||||
|
iter, err = aclTokenListByRole(tx, params.Role, params.EnterpriseMeta)
|
||||||
needLocalityFilter = true
|
needLocalityFilter = true
|
||||||
|
|
||||||
} else if policy == "" && role == "" && methodName != "" {
|
} else if params.Policy == "" && params.Role == "" && params.MethodName != "" && params.ServiceName == "" {
|
||||||
iter, err = aclTokenListByAuthMethod(tx, methodName, methodMeta, entMeta)
|
// Find by methodName
|
||||||
|
iter, err = aclTokenListByAuthMethod(tx, params.MethodName, params.MethodMeta, params.EnterpriseMeta)
|
||||||
|
needLocalityFilter = true
|
||||||
|
|
||||||
|
} else if params.Policy == "" && params.Role == "" && params.MethodName == "" && params.ServiceName != "" {
|
||||||
|
// Find by the service identity's serviceName
|
||||||
|
iter, err = aclTokenListByServiceName(tx, params.ServiceName, params.EnterpriseMeta)
|
||||||
needLocalityFilter = true
|
needLocalityFilter = true
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return 0, nil, fmt.Errorf("can only filter by one of policy, role, or methodName at a time")
|
return 0, nil, fmt.Errorf("can only filter by one of policy, role, serviceName, or methodName at a time")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("failed acl token lookup: %v", err)
|
return 0, nil, fmt.Errorf("failed acl token lookup: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if needLocalityFilter && global != local {
|
if needLocalityFilter && params.Global != params.Local {
|
||||||
iter = memdb.NewFilterIterator(iter, func(raw interface{}) bool {
|
iter = memdb.NewFilterIterator(iter, func(raw interface{}) bool {
|
||||||
token, ok := raw.(*structs.ACLToken)
|
token, ok := raw.(*structs.ACLToken)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if global && !token.Local {
|
if params.Global && !token.Local {
|
||||||
return false
|
return false
|
||||||
} else if local && token.Local {
|
} else if params.Local && token.Local {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,7 +733,7 @@ func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the table index.
|
// Get the table index.
|
||||||
idx := aclTokenMaxIndex(tx, nil, entMeta)
|
idx := aclTokenMaxIndex(tx, nil, params.EnterpriseMeta)
|
||||||
return idx, result, nil
|
return idx, result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,10 @@ func aclTokenListByAuthMethod(tx ReadTxn, authMethod string, _, _ *acl.Enterpris
|
||||||
return tx.Get(tableACLTokens, indexAuthMethod, AuthMethodQuery{Value: authMethod})
|
return tx.Get(tableACLTokens, indexAuthMethod, AuthMethodQuery{Value: authMethod})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func aclTokenListByServiceName(tx ReadTxn, serviceName string, entMeta *acl.EnterpriseMeta) (memdb.ResultIterator, error) {
|
||||||
|
return tx.Get(tableACLTokens, indexServiceName, Query{Value: serviceName})
|
||||||
|
}
|
||||||
|
|
||||||
func aclTokenDeleteWithToken(tx WriteTxn, token *structs.ACLToken, idx uint64) error {
|
func aclTokenDeleteWithToken(tx WriteTxn, token *structs.ACLToken, idx uint64) error {
|
||||||
// remove the token
|
// remove the token
|
||||||
if err := tx.Delete(tableACLTokens, token); err != nil {
|
if err := tx.Delete(tableACLTokens, token); err != nil {
|
||||||
|
|
|
@ -22,6 +22,7 @@ const (
|
||||||
indexAccessor = "accessor"
|
indexAccessor = "accessor"
|
||||||
indexPolicies = "policies"
|
indexPolicies = "policies"
|
||||||
indexRoles = "roles"
|
indexRoles = "roles"
|
||||||
|
indexServiceName = "service-name"
|
||||||
indexAuthMethod = "authmethod"
|
indexAuthMethod = "authmethod"
|
||||||
indexLocality = "locality"
|
indexLocality = "locality"
|
||||||
indexName = "name"
|
indexName = "name"
|
||||||
|
@ -106,6 +107,15 @@ func tokensTableSchema() *memdb.TableSchema {
|
||||||
writeIndex: indexExpiresLocalFromACLToken,
|
writeIndex: indexExpiresLocalFromACLToken,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
indexServiceName: {
|
||||||
|
Name: indexServiceName,
|
||||||
|
AllowMissing: true,
|
||||||
|
Unique: false,
|
||||||
|
Indexer: indexerMulti[Query, *structs.ACLToken]{
|
||||||
|
readIndex: indexFromQuery,
|
||||||
|
writeIndexMulti: indexServiceNameFromACLToken,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,6 +408,21 @@ func indexExpiresFromACLToken(t *structs.ACLToken, local bool) ([]byte, error) {
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func indexServiceNameFromACLToken(token *structs.ACLToken) ([][]byte, error) {
|
||||||
|
vals := make([][]byte, 0, len(token.ServiceIdentities))
|
||||||
|
for _, id := range token.ServiceIdentities {
|
||||||
|
if id != nil && id.ServiceName != "" {
|
||||||
|
var b indexBuilder
|
||||||
|
b.String(strings.ToLower(id.ServiceName))
|
||||||
|
vals = append(vals, b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return nil, errMissingValueForIndex
|
||||||
|
}
|
||||||
|
return vals, nil
|
||||||
|
}
|
||||||
|
|
||||||
func authMethodsTableSchema() *memdb.TableSchema {
|
func authMethodsTableSchema() *memdb.TableSchema {
|
||||||
return &memdb.TableSchema{
|
return &memdb.TableSchema{
|
||||||
Name: tableACLAuthMethods,
|
Name: tableACLAuthMethods,
|
||||||
|
|
|
@ -214,6 +214,7 @@ func TestStateStore_ACLBootstrap(t *testing.T) {
|
||||||
require.Equal(t, uint64(3), index)
|
require.Equal(t, uint64(3), index)
|
||||||
|
|
||||||
// Make sure the ACLs are in an expected state.
|
// Make sure the ACLs are in an expected state.
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, tokens, 1)
|
require.Len(t, tokens, 1)
|
||||||
|
@ -228,6 +229,7 @@ func TestStateStore_ACLBootstrap(t *testing.T) {
|
||||||
err = s.ACLBootstrap(32, index, token2.Clone())
|
err = s.ACLBootstrap(32, index, token2.Clone())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, tokens, 2)
|
require.Len(t, tokens, 2)
|
||||||
|
@ -849,6 +851,23 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
AuthMethod: "test",
|
AuthMethod: "test",
|
||||||
Local: true,
|
Local: true,
|
||||||
},
|
},
|
||||||
|
// the serviceName specific token
|
||||||
|
&structs.ACLToken{
|
||||||
|
AccessorID: "80c900e1-2fc5-4685-ae29-1b2d17fc30e4",
|
||||||
|
SecretID: "9d229cfd-ec4b-4d31-a6fd-ecbcb2a41d41",
|
||||||
|
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||||
|
{ServiceName: "sn1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// the serviceName specific token and local
|
||||||
|
&structs.ACLToken{
|
||||||
|
AccessorID: "a14fa45e-0afe-4b44-961d-a430030ccfe2",
|
||||||
|
SecretID: "17f696b9-448a-4bd3-936b-08c92c66530f",
|
||||||
|
ServiceIdentities: []*structs.ACLServiceIdentity{
|
||||||
|
{ServiceName: "sn1"},
|
||||||
|
},
|
||||||
|
Local: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, s.ACLTokenBatchSet(2, tokens, ACLTokenSetOptions{}))
|
require.NoError(t, s.ACLTokenBatchSet(2, tokens, ACLTokenSetOptions{}))
|
||||||
|
@ -860,6 +879,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
policy string
|
policy string
|
||||||
role string
|
role string
|
||||||
methodName string
|
methodName string
|
||||||
|
serviceName string
|
||||||
accessors []string
|
accessors []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,6 +896,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
|
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
|
||||||
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
|
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
|
||||||
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
||||||
|
"80c900e1-2fc5-4685-ae29-1b2d17fc30e4", // serviceName + global
|
||||||
"a7715fde-8954-4c92-afbc-d84c6ecdc582", // role + global
|
"a7715fde-8954-4c92-afbc-d84c6ecdc582", // role + global
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -889,6 +910,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
accessors: []string{
|
accessors: []string{
|
||||||
"211f0360-ef53-41d3-9d4d-db84396eb6c0", // authMethod + local
|
"211f0360-ef53-41d3-9d4d-db84396eb6c0", // authMethod + local
|
||||||
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
|
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
|
||||||
|
"a14fa45e-0afe-4b44-961d-a430030ccfe2", // serviceName + local
|
||||||
"cadb4f13-f62a-49ab-ab3f-5a7e01b925d9", // role + local
|
"cadb4f13-f62a-49ab-ab3f-5a7e01b925d9", // role + local
|
||||||
"f1093997-b6c7-496d-bfb8-6b1b1895641b", // mgmt + local
|
"f1093997-b6c7-496d-bfb8-6b1b1895641b", // mgmt + local
|
||||||
},
|
},
|
||||||
|
@ -983,6 +1005,30 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ServiceName - Local",
|
||||||
|
local: true,
|
||||||
|
global: false,
|
||||||
|
policy: "",
|
||||||
|
role: "",
|
||||||
|
methodName: "",
|
||||||
|
serviceName: "sn1",
|
||||||
|
accessors: []string{
|
||||||
|
"a14fa45e-0afe-4b44-961d-a430030ccfe2", // serviceName + local
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ServiceName - Global",
|
||||||
|
local: false,
|
||||||
|
global: true,
|
||||||
|
policy: "",
|
||||||
|
role: "",
|
||||||
|
methodName: "",
|
||||||
|
serviceName: "sn1",
|
||||||
|
accessors: []string{
|
||||||
|
"80c900e1-2fc5-4685-ae29-1b2d17fc30e4", // serviceName + global
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "All",
|
name: "All",
|
||||||
local: true,
|
local: true,
|
||||||
|
@ -997,6 +1043,8 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
|
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
|
||||||
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
|
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
|
||||||
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
|
||||||
|
"80c900e1-2fc5-4685-ae29-1b2d17fc30e4", // serviceName + global
|
||||||
|
"a14fa45e-0afe-4b44-961d-a430030ccfe2", // serviceName + local
|
||||||
"a7715fde-8954-4c92-afbc-d84c6ecdc582", // role + global
|
"a7715fde-8954-4c92-afbc-d84c6ecdc582", // role + global
|
||||||
"cadb4f13-f62a-49ab-ab3f-5a7e01b925d9", // role + local
|
"cadb4f13-f62a-49ab-ab3f-5a7e01b925d9", // role + local
|
||||||
"f1093997-b6c7-496d-bfb8-6b1b1895641b", // mgmt + local
|
"f1093997-b6c7-496d-bfb8-6b1b1895641b", // mgmt + local
|
||||||
|
@ -1004,14 +1052,27 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range []struct{ policy, role, methodName string }{
|
for _, tc := range []struct{ policy, role, methodName, serviceName string }{
|
||||||
{testPolicyID_A, testRoleID_A, "test"},
|
{testPolicyID_A, testRoleID_A, "test", ""},
|
||||||
{"", testRoleID_A, "test"},
|
{"", testRoleID_A, "test", ""},
|
||||||
{testPolicyID_A, "", "test"},
|
{testPolicyID_A, "", "test", ""},
|
||||||
{testPolicyID_A, testRoleID_A, ""},
|
{testPolicyID_A, testRoleID_A, "", ""},
|
||||||
|
{testPolicyID_A, "", "", "test"},
|
||||||
} {
|
} {
|
||||||
t.Run(fmt.Sprintf("can't filter on more than one: %s/%s/%s", tc.policy, tc.role, tc.methodName), func(t *testing.T) {
|
t.Run(fmt.Sprintf("can't filter on more than one: %s/%s/%s/%s", tc.policy, tc.role, tc.methodName, tc.serviceName), func(t *testing.T) {
|
||||||
_, _, err := s.ACLTokenList(nil, false, false, tc.policy, tc.role, tc.methodName, nil, nil)
|
var err error
|
||||||
|
if tc.serviceName == "" {
|
||||||
|
// The legacy call can only be tested when the serviceName is not specified
|
||||||
|
// nolint:staticcheck
|
||||||
|
_, _, err = s.ACLTokenList(nil, false, false, tc.policy, tc.role, tc.methodName, nil, nil)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
_, _, err = s.ACLTokenListWithParameters(nil, ACLTokenListParameters{
|
||||||
|
Policy: tc.policy,
|
||||||
|
Role: tc.role,
|
||||||
|
MethodName: tc.methodName,
|
||||||
|
ServiceName: tc.serviceName,
|
||||||
|
})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1020,6 +1081,9 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
tc := tc // capture range variable
|
tc := tc // capture range variable
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
// Test old function
|
||||||
|
if tc.serviceName == "" {
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err := s.ACLTokenList(nil, tc.local, tc.global, tc.policy, tc.role, tc.methodName, nil, nil)
|
_, tokens, err := s.ACLTokenList(nil, tc.local, tc.global, tc.policy, tc.role, tc.methodName, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, tokens, len(tc.accessors))
|
require.Len(t, tokens, len(tc.accessors))
|
||||||
|
@ -1027,6 +1091,24 @@ func TestStateStore_ACLToken_List(t *testing.T) {
|
||||||
for i, token := range tokens {
|
for i, token := range tokens {
|
||||||
require.Equal(t, tc.accessors[i], token.AccessorID)
|
require.Equal(t, tc.accessors[i], token.AccessorID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Test new function
|
||||||
|
{
|
||||||
|
_, tokens, err := s.ACLTokenListWithParameters(nil, ACLTokenListParameters{
|
||||||
|
Local: tc.local,
|
||||||
|
Global: tc.global,
|
||||||
|
Policy: tc.policy,
|
||||||
|
Role: tc.role,
|
||||||
|
ServiceName: tc.serviceName,
|
||||||
|
MethodName: tc.methodName,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, tokens, len(tc.accessors))
|
||||||
|
tokens.Sort()
|
||||||
|
for i, token := range tokens {
|
||||||
|
require.Equal(t, tc.accessors[i], token.AccessorID)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1080,6 +1162,7 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) {
|
||||||
require.Equal(t, "node-read-renamed", retrieved.Policies[0].Name)
|
require.Equal(t, "node-read-renamed", retrieved.Policies[0].Name)
|
||||||
|
|
||||||
// list tokens without stale links
|
// list tokens without stale links
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -1124,6 +1207,7 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) {
|
||||||
require.Len(t, retrieved.Policies, 0)
|
require.Len(t, retrieved.Policies, 0)
|
||||||
|
|
||||||
// list tokens without stale links
|
// list tokens without stale links
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -1209,6 +1293,7 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) {
|
||||||
require.Equal(t, "node-read-role-renamed", retrieved.Roles[0].Name)
|
require.Equal(t, "node-read-role-renamed", retrieved.Roles[0].Name)
|
||||||
|
|
||||||
// list tokens without stale links
|
// list tokens without stale links
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -1253,6 +1338,7 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) {
|
||||||
require.Len(t, retrieved.Roles, 0)
|
require.Len(t, retrieved.Roles, 0)
|
||||||
|
|
||||||
// list tokens without stale links
|
// list tokens without stale links
|
||||||
|
// nolint:staticcheck
|
||||||
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
_, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -2688,16 +2774,19 @@ func TestStateStore_ACLAuthMethod_GlobalNameShadowing_TokenTest(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, t.Run("list local only", func(t *testing.T) {
|
require.True(t, t.Run("list local only", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, true, false, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, true, false, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []string{methodDC2_tok1, methodDC2_tok2}, toList(got))
|
require.ElementsMatch(t, []string{methodDC2_tok1, methodDC2_tok2}, toList(got))
|
||||||
}))
|
}))
|
||||||
require.True(t, t.Run("list global only", func(t *testing.T) {
|
require.True(t, t.Run("list global only", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, false, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, false, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
||||||
}))
|
}))
|
||||||
require.True(t, t.Run("list both", func(t *testing.T) {
|
require.True(t, t.Run("list both", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, true, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, true, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2, methodDC2_tok1, methodDC2_tok2}, toList(got))
|
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2, methodDC2_tok1, methodDC2_tok2}, toList(got))
|
||||||
|
@ -2709,16 +2798,19 @@ func TestStateStore_ACLAuthMethod_GlobalNameShadowing_TokenTest(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
require.True(t, t.Run("list local only (after dc2 delete)", func(t *testing.T) {
|
require.True(t, t.Run("list local only (after dc2 delete)", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, true, false, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, true, false, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Empty(t, got)
|
require.Empty(t, got)
|
||||||
}))
|
}))
|
||||||
require.True(t, t.Run("list global only (after dc2 delete)", func(t *testing.T) {
|
require.True(t, t.Run("list global only (after dc2 delete)", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, false, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, false, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
||||||
}))
|
}))
|
||||||
require.True(t, t.Run("list both (after dc2 delete)", func(t *testing.T) {
|
require.True(t, t.Run("list both (after dc2 delete)", func(t *testing.T) {
|
||||||
|
// nolint:staticcheck
|
||||||
_, got, err := s.ACLTokenList(nil, true, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
_, got, err := s.ACLTokenList(nil, true, true, "", "", "test", defaultEntMeta, defaultEntMeta)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
require.ElementsMatch(t, []string{methodDC1_tok1, methodDC1_tok2}, toList(got))
|
||||||
|
@ -3509,6 +3601,7 @@ func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) {
|
||||||
require.NoError(t, s.ACLRoleBatchSet(2, roles, false))
|
require.NoError(t, s.ACLRoleBatchSet(2, roles, false))
|
||||||
|
|
||||||
// Read the restored ACLs back out and verify that they match.
|
// Read the restored ACLs back out and verify that they match.
|
||||||
|
// nolint:staticcheck
|
||||||
idx, res, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
idx, res, err := s.ACLTokenList(nil, true, true, "", "", "", nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, uint64(4), idx)
|
require.Equal(t, uint64(4), idx)
|
||||||
|
|
|
@ -1305,6 +1305,7 @@ type ACLTokenListRequest struct {
|
||||||
Policy string // Policy filter
|
Policy string // Policy filter
|
||||||
Role string // Role filter
|
Role string // Role filter
|
||||||
AuthMethod string // Auth Method filter
|
AuthMethod string // Auth Method filter
|
||||||
|
ServiceName string // Service name (from service identities) filter
|
||||||
Datacenter string // The datacenter to perform the request within
|
Datacenter string // The datacenter to perform the request within
|
||||||
ACLAuthMethodEnterpriseMeta
|
ACLAuthMethodEnterpriseMeta
|
||||||
acl.EnterpriseMeta
|
acl.EnterpriseMeta
|
||||||
|
|
45
api/acl.go
45
api/acl.go
|
@ -272,6 +272,13 @@ type ACLAuthMethod struct {
|
||||||
Partition string `json:",omitempty"`
|
Partition string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ACLTokenFilterOptions struct {
|
||||||
|
AuthMethod string `json:",omitempty"`
|
||||||
|
Policy string `json:",omitempty"`
|
||||||
|
Role string `json:",omitempty"`
|
||||||
|
ServiceName string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (m *ACLAuthMethod) MarshalJSON() ([]byte, error) {
|
func (m *ACLAuthMethod) MarshalJSON() ([]byte, error) {
|
||||||
type Alias ACLAuthMethod
|
type Alias ACLAuthMethod
|
||||||
exported := &struct {
|
exported := &struct {
|
||||||
|
@ -895,6 +902,44 @@ func (a *ACL) TokenList(q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, erro
|
||||||
return entries, qm, nil
|
return entries, qm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TokenListFiltered lists all tokens that match the given filter options.
|
||||||
|
// The listing does not contain any SecretIDs as those may only be retrieved by a call to TokenRead.
|
||||||
|
func (a *ACL) TokenListFiltered(t ACLTokenFilterOptions, q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, error) {
|
||||||
|
r := a.c.newRequest("GET", "/v1/acl/tokens")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
|
||||||
|
if t.AuthMethod != "" {
|
||||||
|
r.params.Set("authmethod", t.AuthMethod)
|
||||||
|
}
|
||||||
|
if t.Policy != "" {
|
||||||
|
r.params.Set("policy", t.Policy)
|
||||||
|
}
|
||||||
|
if t.Role != "" {
|
||||||
|
r.params.Set("role", t.Role)
|
||||||
|
}
|
||||||
|
if t.ServiceName != "" {
|
||||||
|
r.params.Set("servicename", t.ServiceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 entries []*ACLTokenListEntry
|
||||||
|
if err := decodeBody(resp, &entries); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return entries, qm, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PolicyCreate will create a new policy. It is not allowed for the policy parameters
|
// PolicyCreate will create a new policy. It is not allowed for the policy parameters
|
||||||
// ID field to be set as this will be generated by Consul while processing the request.
|
// ID field to be set as this will be generated by Consul while processing the request.
|
||||||
func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
|
func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
|
||||||
|
|
|
@ -553,6 +553,89 @@ func TestAPI_ACLToken_List(t *testing.T) {
|
||||||
require.NotNil(t, token5)
|
require.NotNil(t, token5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPI_ACLToken_ListFiltered(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
c, s := makeACLClient(t)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
acl := c.ACL()
|
||||||
|
s.WaitForSerfCheck(t)
|
||||||
|
|
||||||
|
created1, _, err := acl.TokenCreate(&ACLToken{
|
||||||
|
Description: "token1",
|
||||||
|
ServiceIdentities: []*ACLServiceIdentity{
|
||||||
|
{ServiceName: "s1"},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, created1)
|
||||||
|
require.NotEqual(t, "", created1.AccessorID)
|
||||||
|
require.NotEqual(t, "", created1.SecretID)
|
||||||
|
|
||||||
|
created2, _, err := acl.TokenCreate(&ACLToken{
|
||||||
|
Description: "token2",
|
||||||
|
ServiceIdentities: []*ACLServiceIdentity{
|
||||||
|
{ServiceName: "s2"},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, created2)
|
||||||
|
require.NotEqual(t, "", created2.AccessorID)
|
||||||
|
require.NotEqual(t, "", created2.SecretID)
|
||||||
|
|
||||||
|
created3, _, err := acl.TokenCreate(&ACLToken{
|
||||||
|
Description: "token3",
|
||||||
|
ServiceIdentities: []*ACLServiceIdentity{
|
||||||
|
{ServiceName: "s1"},
|
||||||
|
{ServiceName: "s2"},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, created3)
|
||||||
|
require.NotEqual(t, "", created3.AccessorID)
|
||||||
|
require.NotEqual(t, "", created3.SecretID)
|
||||||
|
|
||||||
|
tokens, qm, err := acl.TokenListFiltered(ACLTokenFilterOptions{
|
||||||
|
ServiceName: "s1",
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, 0, qm.LastIndex)
|
||||||
|
require.True(t, qm.KnownLeader)
|
||||||
|
require.Len(t, tokens, 2)
|
||||||
|
found := make([]string, 0, 2)
|
||||||
|
for _, token := range tokens {
|
||||||
|
found = append(found, token.Description)
|
||||||
|
}
|
||||||
|
require.ElementsMatch(t, []string{"token1", "token3"}, found)
|
||||||
|
|
||||||
|
tokens, qm, err = acl.TokenListFiltered(ACLTokenFilterOptions{
|
||||||
|
ServiceName: "s2",
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, 0, qm.LastIndex)
|
||||||
|
require.True(t, qm.KnownLeader)
|
||||||
|
require.Len(t, tokens, 2)
|
||||||
|
found = make([]string, 0, 2)
|
||||||
|
for _, token := range tokens {
|
||||||
|
found = append(found, token.Description)
|
||||||
|
}
|
||||||
|
require.ElementsMatch(t, []string{"token2", "token3"}, found)
|
||||||
|
|
||||||
|
tokens, qm, err = acl.TokenListFiltered(ACLTokenFilterOptions{
|
||||||
|
ServiceName: "nothing",
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEqual(t, 0, qm.LastIndex)
|
||||||
|
require.True(t, qm.KnownLeader)
|
||||||
|
require.Empty(t, tokens)
|
||||||
|
|
||||||
|
_, _, err = acl.TokenListFiltered(ACLTokenFilterOptions{
|
||||||
|
ServiceName: "s",
|
||||||
|
AuthMethod: "a",
|
||||||
|
}, nil)
|
||||||
|
require.ErrorContains(t, err, "can only filter by one of")
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPI_ACLToken_Clone(t *testing.T) {
|
func TestAPI_ACLToken_Clone(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
c, s := makeACLClient(t)
|
c, s := makeACLClient(t)
|
||||||
|
|
|
@ -682,6 +682,9 @@ The corresponding CLI command is [`consul acl token list`](/consul/commands/acl/
|
||||||
- `role` `(string: "")` - Filters the token list to those tokens that are
|
- `role` `(string: "")` - Filters the token list to those tokens that are
|
||||||
linked with this specific role ID.
|
linked with this specific role ID.
|
||||||
|
|
||||||
|
- `servicename` `(string: "")` - Filters the token list to those tokens that are
|
||||||
|
linked with this specific service name in their service identity.
|
||||||
|
|
||||||
- `authmethod` `(string: "")` - Filters the token list to those tokens that are
|
- `authmethod` `(string: "")` - Filters the token list to those tokens that are
|
||||||
linked with this specific named auth method.
|
linked with this specific named auth method.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue