Skip to content

Commit

Permalink
Add PolicyReadByName for API (#6615)
Browse files Browse the repository at this point in the history
  • Loading branch information
abaez authored and mkeeler committed Mar 25, 2020
1 parent 9c40fed commit f5773f3
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 4 deletions.
22 changes: 20 additions & 2 deletions agent/acl_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func (s *HTTPServer) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request)

switch req.Method {
case "GET":
fn = s.ACLPolicyRead
fn = s.ACLPolicyReadByID

case "PUT":
fn = s.ACLPolicyWrite
Expand All @@ -237,10 +237,11 @@ func (s *HTTPServer) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request)
return fn(resp, req, policyID)
}

func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) {
func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, policyID, policyName string) (interface{}, error) {
args := structs.ACLPolicyGetRequest{
Datacenter: s.agent.config.Datacenter,
PolicyID: policyID,
PolicyName: policyName,
}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
Expand Down Expand Up @@ -268,6 +269,23 @@ func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request,
return out.Policy, nil
}

func (s *HTTPServer) ACLPolicyReadByName(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if s.checkACLDisabled(resp, req) {
return nil, nil
}

policyName := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/name/")
if policyName == "" {
return nil, BadRequestError{Reason: "Missing policy Name"}
}

return s.ACLPolicyRead(resp, req, "", policyName)
}

func (s *HTTPServer) ACLPolicyReadByID(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) {
return s.ACLPolicyRead(resp, req, policyID, "")
}

func (s *HTTPServer) ACLPolicyCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if s.checkACLDisabled(resp, req) {
return nil, nil
Expand Down
11 changes: 11 additions & 0 deletions agent/acl_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,17 @@ func TestACL_HTTP(t *testing.T) {
require.True(t, ok)
require.Equal(t, policyMap[idMap["policy-read-all-nodes"]], policy)
})

t.Run("Read Name", func(t *testing.T) {
policyName := "read-all-nodes"
req, _ := http.NewRequest("GET", "/v1/acl/policy/name/"+policyName+"?token=root", nil)
resp := httptest.NewRecorder()
raw, err := a.srv.ACLPolicyReadByName(resp, req)
require.NoError(t, err)
policy, ok := raw.(*structs.ACLPolicy)
require.True(t, ok)
require.Equal(t, policyMap[idMap["policy-"+policyName]], policy)
})
})

t.Run("Role", func(t *testing.T) {
Expand Down
11 changes: 10 additions & 1 deletion agent/consul/acl_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,16 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo

return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
func(ws memdb.WatchSet, state *state.Store) error {
index, policy, err := state.ACLPolicyGetByID(ws, args.PolicyID, &args.EnterpriseMeta)
var (
index uint64
policy *structs.ACLPolicy
err error
)
if args.PolicyID != "" {
index, policy, err = state.ACLPolicyGetByID(ws, args.PolicyID, &args.EnterpriseMeta)
} else {
index, policy, err = state.ACLPolicyGetByName(ws, args.PolicyName, &args.EnterpriseMeta)
}

if err != nil {
return err
Expand Down
39 changes: 39 additions & 0 deletions agent/consul/acl_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,45 @@ func TestACLEndpoint_PolicyRead(t *testing.T) {
}
}

func TestACLEndpoint_PolicyReadByName(t *testing.T) {
t.Parallel()
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
codec := rpcClient(t, s1)
defer codec.Close()

testrpc.WaitForLeader(t, s1.RPC, "dc1")

policy, err := upsertTestPolicy(codec, "root", "dc1")
if err != nil {
t.Fatalf("err: %v", err)
}

acl := ACL{srv: s1}

req := structs.ACLPolicyGetRequest{
Datacenter: "dc1",
PolicyName: policy.Name,
QueryOptions: structs.QueryOptions{Token: "root"},
}

resp := structs.ACLPolicyResponse{}

err = acl.PolicyRead(&req, &resp)
if err != nil {
t.Fatalf("err: %v", err)
}

if !reflect.DeepEqual(resp.Policy, policy) {
t.Fatalf("tokens are not equal: %v != %v", resp.Policy, policy)
}
}

func TestACLEndpoint_PolicyBatchRead(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions agent/http_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func init() {
registerEndpoint("/v1/acl/policies", []string{"GET"}, (*HTTPServer).ACLPolicyList)
registerEndpoint("/v1/acl/policy", []string{"PUT"}, (*HTTPServer).ACLPolicyCreate)
registerEndpoint("/v1/acl/policy/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLPolicyCRUD)
registerEndpoint("/v1/acl/policy/name/", []string{"GET"}, (*HTTPServer).ACLPolicyReadByName)
registerEndpoint("/v1/acl/roles", []string{"GET"}, (*HTTPServer).ACLRoleList)
registerEndpoint("/v1/acl/role", []string{"PUT"}, (*HTTPServer).ACLRoleCreate)
registerEndpoint("/v1/acl/role/name/", []string{"GET"}, (*HTTPServer).ACLRoleReadByName)
Expand Down
3 changes: 2 additions & 1 deletion agent/structs/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -1233,7 +1233,8 @@ func (r *ACLPolicyDeleteRequest) RequestDatacenter() string {

// ACLPolicyGetRequest is used at the RPC layer to perform policy read operations
type ACLPolicyGetRequest struct {
PolicyID string // id used for the policy lookup
PolicyID string // id used for the policy lookup (one of PolicyID or PolicyName is allowed)
PolicyName string // name used for the policy lookup (one of PolicyID or PolicyName is allowed)
Datacenter string // The datacenter to perform the request within
EnterpriseMeta
QueryOptions
Expand Down
26 changes: 26 additions & 0 deletions api/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,32 @@ func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMe
return &out, qm, nil
}

// PolicyReadByName retrieves the policy details including the rule set with name.
func (a *ACL) PolicyReadByName(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
r := a.c.newRequest("GET", "/v1/acl/policy/name/"+url.QueryEscape(policyName))
r.setQueryOptions(q)
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()

qm := &QueryMeta{}
parseQueryMeta(resp, qm)
qm.RequestTime = rtt

if !found {
return nil, qm, nil
}

var out ACLPolicy
if err := decodeBody(resp, &out); err != nil {
return nil, nil, err
}

return &out, qm, nil
}

// PolicyList retrieves a listing of all policies. The listing does not include the
// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {
Expand Down
35 changes: 35 additions & 0 deletions api/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,41 @@ func TestAPI_ACLPolicy_CreateReadDelete(t *testing.T) {
require.Error(t, err)
}

func TestAPI_ACLPolicy_CreateReadByNameDelete(t *testing.T) {
t.Parallel()
c, s := makeACLClient(t)
defer s.Stop()

acl := c.ACL()

created, wm, err := acl.PolicyCreate(&ACLPolicy{
Name: "test-policy",
Description: "test-policy description",
Rules: `node_prefix "" { policy = "read" }`,
Datacenters: []string{"dc1"},
}, nil)

require.NoError(t, err)
require.NotNil(t, created)
require.NotEqual(t, "", created.ID)
require.NotEqual(t, 0, wm.RequestTime)

read, qm, err := acl.PolicyReadByName(created.Name, nil)
require.NoError(t, err)
require.NotEqual(t, 0, qm.LastIndex)
require.True(t, qm.KnownLeader)

require.Equal(t, created, read)

wm, err = acl.PolicyDelete(created.ID, nil)
require.NoError(t, err)
require.NotEqual(t, 0, wm.RequestTime)

read, _, err = acl.PolicyRead(created.ID, nil)
require.Nil(t, read)
require.Error(t, err)
}

func TestAPI_ACLPolicy_CreateUpdate(t *testing.T) {
t.Parallel()
c, s := makeACLClient(t)
Expand Down

0 comments on commit f5773f3

Please sign in to comment.