Skip to content

Commit

Permalink
Merge pull request #50 from spiffe/feature/policies
Browse files Browse the repository at this point in the history
YOLO!

Introuducing policies
  • Loading branch information
v0lkan authored Nov 26, 2024
2 parents 6dbac1f + aacf9fc commit 1cd5070
Show file tree
Hide file tree
Showing 73 changed files with 2,148 additions and 396 deletions.
119 changes: 119 additions & 0 deletions app/nexus/internal/route/acl/policy/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// \\ SPIKE: Secure your secrets with SPIFFE.
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package policy

import (
"errors"
"net/http"
"time"

state "github.com/spiffe/spike/app/nexus/internal/state/base"
"github.com/spiffe/spike/internal/entity/data"
"github.com/spiffe/spike/internal/entity/v1/reqres"
"github.com/spiffe/spike/internal/log"
"github.com/spiffe/spike/internal/net"
)

// RoutePutPolicy handles HTTP PUT requests for creating new policies.
// It processes the request body to create a policy with the specified name,
// SPIFFE ID pattern, path pattern, and permissions.
//
// The function expects a JSON request body containing:
// - Name: policy name
// - SpiffeIdPattern: SPIFFE ID matching pattern
// - PathPattern: path matching pattern
// - Permissions: set of allowed permissions
//
// On success, it returns a JSON response with the created policy's ID.
// On failure, it returns an appropriate error response with status code.
//
// Parameters:
// - w: HTTP response writer for sending the response
// - r: HTTP request containing the policy creation data
// - audit: Audit entry for logging the policy creation action
//
// Returns:
// - error: nil on successful policy creation, error otherwise
//
// Example request body:
//
// {
// "name": "example-policy",
// "spiffe_id_pattern": "spiffe://example.org/*/service",
// "path_pattern": "/api/*",
// "permissions": ["read", "write"]
// }
//
// Example success response:
//
// {
// "id": "policy-123"
// }
//
// Example error response:
//
// {
// "err": "Internal server error"
// }
func RoutePutPolicy(
w http.ResponseWriter, r *http.Request, audit *log.AuditEntry,
) error {
log.Log().Info("routePutPolicy", "method", r.Method, "path", r.URL.Path,
"query", r.URL.RawQuery)
audit.Action = log.AuditCreate

requestBody := net.ReadRequestBody(w, r)
if requestBody == nil {
return errors.New("failed to read request body")
}

request := net.HandleRequest[
reqres.PolicyCreateRequest, reqres.PolicyCreateResponse](
requestBody, w,
reqres.PolicyCreateResponse{Err: reqres.ErrBadInput},
)
if request == nil {
return errors.New("failed to parse request body")
}

// TODO: sanitize

name := request.Name
spiffeIdPattern := request.SpiffeIdPattern
pathPattern := request.PathPattern
permissions := request.Permissions

policy, err := state.CreatePolicy(data.Policy{
Id: "",
Name: name,
SpiffeIdPattern: spiffeIdPattern,
PathPattern: pathPattern,
Permissions: permissions,
CreatedAt: time.Time{},
CreatedBy: "",
})
if err != nil {
log.Log().Info("routePutPolicy",
"msg", "Failed to create policy", "err", err)

responseBody := net.MarshalBody(reqres.PolicyCreateResponse{
Err: "Internal server error",
}, w)

net.Respond(http.StatusInternalServerError, responseBody, w)
log.Log().Error("routePutPolicy", "msg", "internal server error")

return err
}

responseBody := net.MarshalBody(reqres.PolicyCreateResponse{
Id: policy.Id,
}, w)

net.Respond(http.StatusOK, responseBody, w)
log.Log().Info("routePutPolicy", "msg", "OK")

return nil
}
103 changes: 103 additions & 0 deletions app/nexus/internal/route/acl/policy/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// \\ SPIKE: Secure your secrets with SPIFFE.
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package policy

import (
"errors"
"net/http"

state "github.com/spiffe/spike/app/nexus/internal/state/base"
"github.com/spiffe/spike/internal/entity/v1/reqres"
"github.com/spiffe/spike/internal/log"
"github.com/spiffe/spike/internal/net"
)

// RouteDeletePolicy handles HTTP DELETE requests to remove existing policies.
// It processes the request body to delete a policy specified by its ID.
//
// The function expects a JSON request body containing:
// - Id: unique identifier of the policy to delete
//
// On success, it returns an empty JSON response with HTTP 200 status.
// On failure, it returns an appropriate error response with status code.
//
// Parameters:
// - w: HTTP response writer for sending the response
// - r: HTTP request containing the policy ID to delete
// - audit: Audit entry for logging the policy deletion action
//
// Returns:
// - error: nil on successful policy deletion, error otherwise
//
// Example request body:
//
// {
// "id": "policy-123"
// }
//
// Example success response:
//
// {}
//
// Example error response:
//
// {
// "err": "Internal server error"
// }
//
// Possible errors:
// - Failed to read request body
// - Failed to parse request body
// - Failed to marshal response body
// - Failed to delete policy (internal server error)
func RouteDeletePolicy(
w http.ResponseWriter, r *http.Request, audit *log.AuditEntry,
) error {
log.Log().Info("routeDeletePolicy", "method", r.Method, "path", r.URL.Path,
"query", r.URL.RawQuery)
audit.Action = log.AuditDelete

requestBody := net.ReadRequestBody(w, r)
if requestBody == nil {
return errors.New("failed to read request body")
}

request := net.HandleRequest[
reqres.PolicyDeleteRequest, reqres.PolicyDeleteResponse](
requestBody, w,
reqres.PolicyDeleteResponse{Err: reqres.ErrBadInput},
)
if request == nil {
return errors.New("failed to parse request body")
}

policyId := request.Id

err := state.DeletePolicy(policyId)
if err != nil {
log.Log().Info("routeDeletePolicy",
"msg", "Failed to delete policy", "err", err)

responseBody := net.MarshalBody(reqres.PolicyDeleteResponse{
Err: "Internal server error",
}, w)
if responseBody == nil {
return errors.New("failed to marshal response body")
}

net.Respond(http.StatusInternalServerError, responseBody, w)
log.Log().Info("routeDeletePolicy", "msg", "internal server error")
return err
}

responseBody := net.MarshalBody(reqres.PolicyDeleteResponse{}, w)
if responseBody == nil {
return errors.New("failed to marshal response body")
}

net.Respond(http.StatusOK, responseBody, w)
log.Log().Info("routeDeletePolicy", "msg", "OK")
return nil
}
96 changes: 96 additions & 0 deletions app/nexus/internal/route/acl/policy/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// \\ SPIKE: Secure your secrets with SPIFFE.
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package policy

import (
"errors"
"net/http"

state "github.com/spiffe/spike/app/nexus/internal/state/base"
"github.com/spiffe/spike/internal/entity/v1/reqres"
"github.com/spiffe/spike/internal/log"
"github.com/spiffe/spike/internal/net"
)

// RouteListPolicies handles HTTP requests to retrieve all existing policies.
// It returns a list of all policies in the system, including their IDs, names,
// SPIFFE ID patterns, path patterns, and permissions.
//
// The function expects an empty JSON request body ({}) and returns an array
// of policy objects.
//
// Parameters:
// - w: HTTP response writer for sending the response
// - r: HTTP request for the policy listing operation
// - audit: Audit entry for logging the policy list action
//
// Returns:
// - error: nil on successful retrieval, error otherwise
//
// Example request body:
//
// {}
//
// Example success response:
//
// {
// "policies": [
// {
// "id": "policy-123",
// "name": "example-policy",
// "spiffe_id_pattern": "spiffe://example.org/*/service",
// "path_pattern": "/api/*",
// "permissions": ["read", "write"],
// "created_at": "2024-01-01T00:00:00Z",
// "created_by": "user-abc"
// }
// // ... additional policies
// ]
// }
//
// Example error response:
//
// {
// "err": "Internal server error"
// }
//
// Possible errors:
// - Failed to read request body
// - Failed to parse request body
// - Failed to marshal response body
func RouteListPolicies(
w http.ResponseWriter, r *http.Request, audit *log.AuditEntry,
) error {
log.Log().Info("routeListPolicies", "method", r.Method, "path", r.URL.Path,
"query", r.URL.RawQuery)
audit.Action = log.AuditList

requestBody := net.ReadRequestBody(w, r)
if requestBody == nil {
return errors.New("failed to read request body")
}

request := net.HandleRequest[
reqres.PolicyListRequest, reqres.PolicyListResponse](
requestBody, w,
reqres.PolicyListResponse{Err: reqres.ErrBadInput},
)
if request == nil {
return errors.New("failed to parse request body")
}

policies := state.ListPolicies()

responseBody := net.MarshalBody(reqres.PolicyListResponse{
Policies: policies,
}, w)
if responseBody == nil {
return errors.New("failed to marshal response body")
}

net.Respond(http.StatusOK, responseBody, w)
log.Log().Info("routeListPolicies", "msg", "success")
return nil
}
Loading

0 comments on commit 1cd5070

Please sign in to comment.