[NET-6103] Enable query tokens by service name using templated policy (#19666)

This commit is contained in:
Ronald 2023-11-16 14:32:06 -05:00 committed by GitHub
parent d9432f9032
commit ea0caa3e0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 133 additions and 7 deletions

3
.changelog/19666.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
api: Add support for listing ACL tokens by service name when using templated policies.
```

View File

@ -1361,6 +1361,39 @@ func TestACL_HTTP(t *testing.T) {
require.Len(t, token.ServiceIdentities, 1)
require.Equal(t, "sn1", token.ServiceIdentities[0].ServiceName)
})
t.Run("List by ServiceName based on templated policies", func(t *testing.T) {
tokenInput := &structs.ACLToken{
Description: "token for templated policies service",
TemplatedPolicies: []*structs.ACLTemplatedPolicy{
{
TemplateName: "builtin/service",
TemplateVariables: &structs.ACLTemplatedPolicyVariables{
Name: "service1",
},
},
},
}
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)
req, _ = http.NewRequest("GET", "/v1/acl/tokens?servicename=service1", 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 templated policies service", token.Description)
require.Len(t, token.TemplatedPolicies, 1)
require.Equal(t, "service1", token.TemplatedPolicies[0].TemplateVariables.Name)
})
})
t.Run("ACLTemplatedPolicy", func(t *testing.T) {

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api"
)
const (
@ -409,7 +410,7 @@ func indexExpiresFromACLToken(t *structs.ACLToken, local bool) ([]byte, error) {
}
func indexServiceNameFromACLToken(token *structs.ACLToken) ([][]byte, error) {
vals := make([][]byte, 0, len(token.ServiceIdentities))
vals := make([][]byte, 0, len(token.ServiceIdentities)+len(token.TemplatedPolicies))
for _, id := range token.ServiceIdentities {
if id != nil && id.ServiceName != "" {
var b indexBuilder
@ -417,6 +418,15 @@ func indexServiceNameFromACLToken(token *structs.ACLToken) ([][]byte, error) {
vals = append(vals, b.Bytes())
}
}
for _, tp := range token.TemplatedPolicies {
if tp != nil && tp.TemplateName == api.ACLTemplatedPolicyServiceName && tp.TemplateVariables != nil && tp.TemplateVariables.Name != "" {
var b indexBuilder
b.String(strings.ToLower(tp.TemplateVariables.Name))
vals = append(vals, b.Bytes())
}
}
if len(vals) == 0 {
return nil, errMissingValueForIndex
}

View File

@ -868,6 +868,33 @@ func TestStateStore_ACLToken_List(t *testing.T) {
},
Local: true,
},
// templated policy: the serviceName specific token
&structs.ACLToken{
AccessorID: "2f89e357-dedb-8d8f-7f30-1f465a41508a",
SecretID: "21ab62c9-5372-038c-b6ba-424961cb38c7",
TemplatedPolicies: []*structs.ACLTemplatedPolicy{
{
TemplateName: "builtin/service",
TemplateVariables: &structs.ACLTemplatedPolicyVariables{
Name: "service-1",
},
},
},
},
// templated policy: the serviceName specific token and local
&structs.ACLToken{
AccessorID: "5e5d6269-f933-3af2-fe30-259b050223f9",
SecretID: "89a456eb-5d55-9a65-92e1-96935dc5b358",
TemplatedPolicies: []*structs.ACLTemplatedPolicy{
{
TemplateName: "builtin/service",
TemplateVariables: &structs.ACLTemplatedPolicyVariables{
Name: "service-1",
},
},
},
Local: true,
},
}
require.NoError(t, s.ACLTokenBatchSet(2, tokens, ACLTokenSetOptions{}))
@ -893,6 +920,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
methodName: "",
accessors: []string{
acl.AnonymousTokenID,
"2f89e357-dedb-8d8f-7f30-1f465a41508a", // templated policy: serviceName + global
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
@ -910,6 +938,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
accessors: []string{
"211f0360-ef53-41d3-9d4d-db84396eb6c0", // authMethod + local
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
"5e5d6269-f933-3af2-fe30-259b050223f9", // templated policies: serviceName + local
"a14fa45e-0afe-4b44-961d-a430030ccfe2", // serviceName + local
"cadb4f13-f62a-49ab-ab3f-5a7e01b925d9", // role + local
"f1093997-b6c7-496d-bfb8-6b1b1895641b", // mgmt + local
@ -1030,18 +1059,58 @@ func TestStateStore_ACLToken_List(t *testing.T) {
},
},
{
name: "All",
local: true,
global: true,
policy: "",
role: "",
methodName: "",
name: "templated policy: ServiceName - Global",
local: false,
global: true,
policy: "",
role: "",
methodName: "",
serviceName: "service-1",
accessors: []string{
"2f89e357-dedb-8d8f-7f30-1f465a41508a", // serviceName + global
},
},
{
name: "templated policy: ServiceName - Local",
local: true,
global: false,
policy: "",
role: "",
methodName: "",
serviceName: "service-1",
accessors: []string{
"5e5d6269-f933-3af2-fe30-259b050223f9", // serviceName + local
},
},
{
name: "templated policy: ServiceName - All",
local: true,
global: true,
policy: "",
role: "",
methodName: "",
serviceName: "service-1",
accessors: []string{
"2f89e357-dedb-8d8f-7f30-1f465a41508a", // serviceName + global
"5e5d6269-f933-3af2-fe30-259b050223f9", // serviceName + local
},
},
{
name: "All",
local: true,
global: true,
policy: "",
role: "",
methodName: "",
serviceName: "",
accessors: []string{
acl.AnonymousTokenID,
"211f0360-ef53-41d3-9d4d-db84396eb6c0", // authMethod + local
"2f89e357-dedb-8d8f-7f30-1f465a41508a", // templated policy: serviceName + global
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
"5e5d6269-f933-3af2-fe30-259b050223f9", // templated policy: serviceName + local
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
"80c900e1-2fc5-4685-ae29-1b2d17fc30e4", // serviceName + global
"a14fa45e-0afe-4b44-961d-a430030ccfe2", // serviceName + local

View File

@ -160,6 +160,17 @@ func (tp *ACLTemplatedPolicy) ValidateTemplatedPolicy(schema string) error {
return fmt.Errorf("failed to load json schema for validation %w", err)
}
// validate service and node identity names
if tp.TemplateVariables != nil {
if tp.TemplateName == api.ACLTemplatedPolicyServiceName && !acl.IsValidServiceIdentityName(tp.TemplateVariables.Name) {
return fmt.Errorf("service identity %q has an invalid name. Only lowercase alphanumeric characters, '-' and '_' are allowed", tp.TemplateVariables.Name)
}
if tp.TemplateName == api.ACLTemplatedPolicyNodeName && !acl.IsValidNodeIdentityName(tp.TemplateVariables.Name) {
return fmt.Errorf("node identity %q has an invalid name. Only lowercase alphanumeric characters, '-' and '_' are allowed", tp.TemplateVariables.Name)
}
}
if res.Valid() {
return nil
}