diff --git a/internal/security/bootstrapper/command/setupacl/aclpolicies.go b/internal/security/bootstrapper/command/setupacl/aclpolicies.go index 569a842f62..cfec47f689 100644 --- a/internal/security/bootstrapper/command/setupacl/aclpolicies.go +++ b/internal/security/bootstrapper/command/setupacl/aclpolicies.go @@ -66,6 +66,7 @@ const ( edgeXServicePolicyName = "edgex-service-policy" consulCreatePolicyAPI = "/v1/acl/policy" + consulPolicyListAPI = "/v1/acl/policies" consulReadPolicyByNameAPI = "/v1/acl/policy/name/%s" aclNotFoundMessage = "ACL not found" @@ -105,6 +106,10 @@ const ( edgeXManagementPolicyName = "edgex-management-policy" ) +type PolicyListResponse []struct { + Name string `json:"Name"` +} + // getOrCreateRegistryPolicy retrieves or creates a new policy // it inserts a new policy if the policy name does not exist and returns a policy // it returns the same policy if the policy name already exists @@ -184,6 +189,15 @@ func (c *cmd) getOrCreateRegistryPolicy(tokenID, policyName, policyRules string) // getPolicyByName gets policy by policy name, returns nil if not found func (c *cmd) getPolicyByName(tokenID, policyName string) (*Policy, error) { + policyExists, err := c.checkPolicyExists(tokenID, policyName) + if err != nil { + return nil, err + } + + if !policyExists { + return nil, nil + } + readPolicyByNameURL, err := c.getRegistryApiUrl(fmt.Sprintf(consulReadPolicyByNameAPI, policyName)) if err != nil { return nil, err @@ -230,3 +244,43 @@ func (c *cmd) getPolicyByName(tokenID, policyName string) (*Policy, error) { resp.StatusCode, string(readPolicyResp)) } } + +func (c *cmd) checkPolicyExists(tokenID, policyName string) (bool, error) { + policyListURL, err := c.getRegistryApiUrl(consulPolicyListAPI) + if err != nil { + return false, err + } + + policyListReq, err := http.NewRequest(http.MethodGet, policyListURL, http.NoBody) + if err != nil { + return false, fmt.Errorf("Failed to prepare policyListReq request for http URL %s: %w", policyListURL, err) + } + + policyListReq.Header.Add(share.ConsulTokenHeader, tokenID) + policyListResp, err := c.client.Do(policyListReq) + if err != nil { + return false, fmt.Errorf("Failed to GET policy list request for http URL %s: %w", policyListURL, err) + } + defer policyListResp.Body.Close() + + var policyList PolicyListResponse + + err = json.NewDecoder(policyListResp.Body).Decode(&policyList) + if err != nil { + return false, fmt.Errorf("Failed to decode policy list reponse: %w", err) + } + + switch policyListResp.StatusCode { + case http.StatusOK: + for _, policy := range policyList { + // consul is case-sensitive + if policy.Name == policyName { + return true, nil + } + } + default: + return false, fmt.Errorf("Failed to get consul policy list from [%s] and status code= %d", consulPolicyListAPI, + policyListResp.StatusCode) + } + return false, nil +} diff --git a/internal/security/bootstrapper/command/setupacl/stubregistryserver_test.go b/internal/security/bootstrapper/command/setupacl/stubregistryserver_test.go index 008dde483a..399217c1ab 100644 --- a/internal/security/bootstrapper/command/setupacl/stubregistryserver_test.go +++ b/internal/security/bootstrapper/command/setupacl/stubregistryserver_test.go @@ -332,6 +332,22 @@ func (registry *registryTestServer) getRegistryServerConf(t *testing.T) *config. w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte("Invalid Policy: A Policy with Name " + edgeXServicePolicyName + " already exists")) } + case consulPolicyListAPI: + require.Equal(t, http.MethodGet, r.Method) + w.WriteHeader(http.StatusOK) + jsonResponse := []map[string]interface{}{ + { + "Name": "global-management", + }, + { + "Name": "node-read", + }, + { + "Name": "test-policy-name", + }, + } + err := json.NewEncoder(w).Encode(jsonResponse) + require.NoError(t, err) default: t.Fatalf("Unexpected call to URL %s", r.URL.EscapedPath()) }