Skip to content

Commit

Permalink
[SECENG-639] Policy bundle commands (#765)
Browse files Browse the repository at this point in the history
  • Loading branch information
sagar-connect authored Aug 11, 2022
1 parent 366cc54 commit e042377
Show file tree
Hide file tree
Showing 17 changed files with 337 additions and 1,086 deletions.
151 changes: 22 additions & 129 deletions api/policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,129 +30,49 @@ type httpError struct {
Context map[string]interface{} `json:"context,omitempty"`
}

// ListPolicies calls the view policy-service list policy API
func (c Client) ListPolicies(ownerID string) (interface{}, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/owner/%s/policy", c.serverUrl, ownerID), nil)
if err != nil {
return nil, fmt.Errorf("failed to construct request: %v", err)
}

resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
var payload httpError
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return nil, fmt.Errorf("unexpected status-code: %d", resp.StatusCode)
}
return nil, fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, payload.Error)
}

var body interface{}
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, fmt.Errorf("failed to decode response body: %v", err)
}

return body, nil
}

// Creation types taken from policy-service: internal/policy/api.go

// CreationRequest represents the json payload to create a Policy in the Policy-Service
type CreationRequest struct {
Context string `json:"context"`
Content string `json:"content"`
// CreatePolicyBundleRequest defines the fields for the Create-Policy-Bundle endpoint as defined in Policy Service
type CreatePolicyBundleRequest struct {
Policies map[string]string `json:"policies"`
}

// CreatePolicy call the Create Policy API in the Policy-Service. It creates a policy for the specified owner and returns the created
// policy response as an interface{}.
func (c Client) CreatePolicy(ownerID string, policy CreationRequest) (interface{}, error) {
// CreatePolicyBundle calls the Create Policy Bundle API in the Policy-Service.
// It creates a policy bundle for the specified owner+context and returns the http status code as response
func (c Client) CreatePolicyBundle(ownerID string, context string, policy CreatePolicyBundleRequest) error {
data, err := json.Marshal(policy)
if err != nil {
return nil, fmt.Errorf("failed to encode policy payload: %w", err)
return fmt.Errorf("failed to encode policy payload: %w", err)
}

req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1/owner/%s/policy", c.serverUrl, ownerID), bytes.NewReader(data))
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1/owner/%s/context/%s/policy-bundle", c.serverUrl, ownerID, context), bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("failed to construct request: %v", err)
return fmt.Errorf("failed to construct request: %v", err)
}

req.Header.Set("Content-Length", strconv.Itoa(len(data)))

resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get response from policy-service: %w", err)
return fmt.Errorf("failed to get response from policy-service: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusCreated {
var response httpError
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("unexpected status-code: %d", resp.StatusCode)
}
return nil, fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, response.Error)
}

var response interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}

return &response, nil
}

type UpdateRequest struct {
Context *string `json:"context,omitempty"`
Content *string `json:"content,omitempty"`
}

// UpdatePolicy calls the UPDATE policy API in the policy-service. It updates a policy in the policy-service matching the given owner-id and policy-id.
func (c Client) UpdatePolicy(ownerID string, policyID string, policy UpdateRequest) (interface{}, error) {
data, err := json.Marshal(policy)
if err != nil {
return nil, fmt.Errorf("failed to encode policy payload: %w", err)
}

req, err := http.NewRequest(
"PATCH",
fmt.Sprintf("%s/api/v1/owner/%s/policy/%s", c.serverUrl, ownerID, policyID),
bytes.NewReader(data),
)
if err != nil {
return nil, fmt.Errorf("failed to construct request: %v", err)
}

req.Header.Set("Content-Length", strconv.Itoa(len(data)))

resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to get response from policy-service: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
var response httpError
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("unexpected status-code: %d", resp.StatusCode)
return fmt.Errorf("unexpected status-code: %d", resp.StatusCode)
}
return nil, fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, response.Error)
}

var response interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
return fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, response.Error)
}

return &response, nil
return nil
}

// GetPolicy calls the GET policy API in the policy-service.It fetches the policy from policy-service matching the given owner-id and policy-id.
// It returns an error if the call fails or the policy could not be found.
func (c Client) GetPolicy(ownerID string, policyID string) (interface{}, error) {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/owner/%s/policy/%s", c.serverUrl, ownerID, policyID), nil)
// FetchPolicyBundle calls the GET policy-bundle API in the policy-service
// If policyName is empty, the full policy bundle would be fetched for given ownerID+context
// If a policyName is provided, only that matching policy would be fetched for given ownerID+context+policyName
func (c Client) FetchPolicyBundle(ownerID, context, policyName string) (interface{}, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/owner/%s/context/%s/policy-bundle/%s", c.serverUrl, ownerID, context, policyName), nil)
if err != nil {
return nil, fmt.Errorf("failed to construct request: %v", err)
}
Expand All @@ -179,32 +99,6 @@ func (c Client) GetPolicy(ownerID string, policyID string) (interface{}, error)
return body, nil
}

// DeletePolicy calls the DELETE Policy API in the policy-service.
// It attempts to delete the policy matching the given policy-id and belonging to the given ownerID.
// It returns an error if the call fails or the policy could not be deleted.
func (c Client) DeletePolicy(ownerID string, policyID string) error {
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v1/owner/%s/policy/%s", c.serverUrl, ownerID, policyID), nil)
if err != nil {
return fmt.Errorf("failed to construct request: %v", err)
}

resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusNoContent {
var payload httpError
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return fmt.Errorf("unexpected status-code: %d", resp.StatusCode)
}
return fmt.Errorf("unexpected status-code: %d - %s", resp.StatusCode, payload.Error)
}

return nil
}

type DecisionQueryRequest struct {
Status string
After *time.Time
Expand All @@ -216,8 +110,8 @@ type DecisionQueryRequest struct {

// GetDecisionLogs calls the GET decision query API of policy-service. The endpoint accepts multiple filter values as
// path query parameters (start-time, end-time, branch-name, project-id and offset).
func (c Client) GetDecisionLogs(ownerID string, request DecisionQueryRequest) ([]interface{}, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/owner/%s/decision", c.serverUrl, ownerID), nil)
func (c Client) GetDecisionLogs(ownerID string, context string, request DecisionQueryRequest) ([]interface{}, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/owner/%s/context/%s/decision", c.serverUrl, ownerID, context), nil)
if err != nil {
return nil, fmt.Errorf("failed to construct request: %v", err)
}
Expand Down Expand Up @@ -270,18 +164,17 @@ func (c Client) GetDecisionLogs(ownerID string, request DecisionQueryRequest) ([
// The context determines which policies to apply.
type DecisionRequest struct {
Input string `json:"input"`
Context string `json:"context"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}

// MakeDecision sends a requests to Policy-Service public decision endpoint and returns the decision response
func (c Client) MakeDecision(ownerID string, req DecisionRequest) (interface{}, error) {
func (c Client) MakeDecision(ownerID string, context string, req DecisionRequest) (interface{}, error) {
payload, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}

endpoint := fmt.Sprintf("%s/api/v1/owner/%s/decision", c.serverUrl, ownerID)
endpoint := fmt.Sprintf("%s/api/v1/owner/%s/context/%s/decision", c.serverUrl, ownerID, context)

request, err := http.NewRequest("POST", endpoint, bytes.NewReader(payload))
if err != nil {
Expand Down
Loading

0 comments on commit e042377

Please sign in to comment.