Skip to content

Commit

Permalink
implement spike list
Browse files Browse the repository at this point in the history
Signed-off-by: Volkan Özçelik <[email protected]>
  • Loading branch information
v0lkan committed Nov 6, 2024
1 parent 1fdedfb commit bf62700
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 18 deletions.
46 changes: 46 additions & 0 deletions app/nexus/internal/route/path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// \\ SPIKE: Secure your secrets with SPIFFE.
// \\\\\ Copyright 2024-present SPIKE contributors.
// \\\\\\\ SPDX-License-Identifier: Apache-2.0

package route

import (
"encoding/json"
"io"
"log"
"net/http"

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

func routeListPaths(r *http.Request, w http.ResponseWriter) {
log.Println("routeListPaths:", r.Method, r.URL.Path, r.URL.RawQuery)

body := net.ReadRequestBody(r, w)
if body == nil {
return
}

var req reqres.SecretListRequest
if err := net.HandleRequestError(w, json.Unmarshal(body, &req)); err != nil {
log.Println("routeListPaths: Problem handling request:", err.Error())
return
}

keys := state.ListKeys()

res := reqres.SecretListResponse{Keys: keys}
md, err := json.Marshal(res)
if err != nil {
log.Println("routeListPaths: Problem generating response:", err.Error())
w.WriteHeader(http.StatusInternalServerError)
}

w.WriteHeader(http.StatusOK)
_, err = io.WriteString(w, string(md))
if err != nil {
log.Println("routeListPaths: Problem writing response:", err.Error())
}
}
2 changes: 2 additions & 0 deletions app/nexus/internal/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func factory(p, a, m string) handler {
return routeDeleteSecret
case m == http.MethodPost && a == "undelete" && p == urlSecrets:
return routeUndeleteSecret
case m == http.MethodPost && a == "list" && p == urlSecrets:
return routeListPaths
// Fallback route.
default:
return routeFallback
Expand Down
41 changes: 41 additions & 0 deletions app/nexus/internal/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,54 @@ func DeleteSecret(path string, versions []int) {
kv.Delete(path, versions)
}

// UndeleteSecret restores previously deleted versions of a secret at the
// specified path. It takes a path string identifying the secret's location and
// a slice of version numbers to restore. The function acquires a lock on the
// key-value store to ensure thread-safe operations during the undelete process.
//
// The function operates synchronously and will block until the undelete operation
// is complete. If any specified version numbers don't exist or were not previously
// deleted, those versions will be silently skipped.
//
// Parameters:
// - path: The path to the secret to be restored
// - versions: A slice of integer version numbers to restore
//
// Example:
//
// // Restore versions 1 and 3 of a secret
// UndeleteSecret("/app/secrets/api-key", []int{1, 3})
func UndeleteSecret(path string, versions []int) {
kvMu.Lock()
defer kvMu.Unlock()

kv.Undelete(path, versions)
}

// ListKeys returns a slice of strings containing all keys currently stored
// in the key-value store. The function acquires a lock on the store to ensure
// a consistent view of the keys during enumeration.
//
// The returned slice contains the paths to all keys, regardless of their
// version status (active or deleted). The paths are returned in lexicographical
// order.
//
// Returns:
// - []string: A slice containing all key paths in the store
//
// Example:
//
// keys := ListKeys()
// for _, key := range keys {
// fmt.Printf("Found key: %s\n", key)
// }
func ListKeys() []string {
kvMu.Lock()
defer kvMu.Unlock()

return kv.List()
}

// GetSecret retrieves a secret from the specified path and version.
// It provides thread-safe read access to the secret store.
//
Expand Down
25 changes: 9 additions & 16 deletions app/spike/internal/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ package cmd

import (
"fmt"

"github.com/spf13/cobra"
"github.com/spiffe/go-spiffe/v2/workloadapi"
"github.com/spiffe/spike/app/spike/internal/net"
)

// NewListCommand creates and returns a new cobra.Command for listing all secret
Expand Down Expand Up @@ -39,22 +39,15 @@ func NewListCommand(source *workloadapi.X509Source) *cobra.Command {
Use: "list",
Short: "List all secret paths",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Listing all secret paths...")
fmt.Println("I mean... It WILL list them once someone implements me.")

// keys := make([]string, 0, len(store.data))
// for k := range store.data {
// keys = append(keys, k)
// }
// if len(keys) == 0 {
// fmt.Println("No secrets found")
// return
// }
// fmt.Println("Secrets:")
// for _, key := range keys {
// fmt.Printf("- %s\n", key)
// }
keys, err := net.ListSecretKeys(source)
if err != nil {
fmt.Println("Error listing secret keys:", err)
return
}

for _, key := range keys {
fmt.Printf("- %s\n", key)
}
},
}

Expand Down
1 change: 1 addition & 0 deletions app/spike/internal/net/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ const urlSecretGet = "https://localhost:8553/v1/secrets?action=get"
const urlSecretPut = "https://localhost:8553/v1/secrets"
const urlSecretDelete = "https://localhost:8553/v1/secrets?action=delete"
const urlSecretUndelete = "https://localhost:8553/v1/secrets?action=undelete"
const urlSecretList = "https://localhost:8553/v1/secrets?action=list"
43 changes: 41 additions & 2 deletions app/spike/internal/net/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,45 @@

package net

func ListSecretKeys() error {
return nil
import (
"encoding/json"
"errors"

"github.com/spiffe/go-spiffe/v2/workloadapi"

"github.com/spiffe/spike/internal/config"
"github.com/spiffe/spike/internal/entity/v1/reqres"
"github.com/spiffe/spike/internal/net"
)

func ListSecretKeys(source *workloadapi.X509Source) ([]string, error) {
r := reqres.SecretListRequest{}
mr, err := json.Marshal(r)
if err != nil {
return []string{}, errors.Join(
errors.New("listSecretKeys: I am having problem generating the payload"),
err,
)
}

client, err := net.CreateMtlsClient(source, config.IsNexus)
if err != nil {
return []string{}, err
}

body, err := net.Post(client, urlSecretList, mr)
if errors.Is(err, net.ErrNotFound) {
return []string{}, nil
}

var res reqres.SecretListResponse
err = json.Unmarshal(body, &res)
if err != nil {
return []string{}, errors.Join(
errors.New("getSecret: Problem parsing response body"),
err,
)
}

return res.Keys, nil
}
4 changes: 4 additions & 0 deletions internal/entity/v1/reqres/reqres.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ type SecretUndeleteResponse struct {
Metadata SecretResponseMetadata `json:"metadata"`
}

// SecretListRequest for listing secrets
type SecretListRequest struct {
}

// SecretListResponse for listing secrets
type SecretListResponse struct {
Keys []string `json:"keys"`
Expand Down

0 comments on commit bf62700

Please sign in to comment.