Skip to content

Commit

Permalink
pdp: server and tool ping functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
magik6k committed Oct 4, 2024
1 parent bdbde39 commit 07a5b22
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 47 deletions.
160 changes: 117 additions & 43 deletions cmd/pdptool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ func main() {
authCreateServiceSecretCmd, // generates pdpservice.json, outputs pubkey
authCreateJWTTokenCmd, // generates jwt token from a secret

pingCmd,

piecePrepareCmd, // hash a piece to get a piece cid
pieceUploadCmd, // upload a piece to a pdp service
},
Expand Down Expand Up @@ -97,72 +99,144 @@ var authCreateServiceSecretCmd = &cli.Command{
var authCreateJWTTokenCmd = &cli.Command{
Name: "create-jwt-token",
Usage: "Generate a JWT token using the service secret",
ArgsUsage: "[service_id]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service-id",
Aliases: []string{"serviceId"},
Usage: "Service ID to include in the JWT token",
Required: true,
},
},
ArgsUsage: "[service_name]",
Action: func(cctx *cli.Context) error {
// Read the private key from pdpservice.json
file, err := os.Open("pdpservice.json")
privKey, err := loadPrivateKey()
if err != nil {
return fmt.Errorf("failed to open pdpservice.json: %v", err)
return err
}
defer file.Close()
var serviceSecret map[string]string
decoder := json.NewDecoder(file)
if err := decoder.Decode(&serviceSecret); err != nil {
return fmt.Errorf("failed to read pdpservice.json: %v", err)

// Get the service name
serviceName := cctx.Args().First()
if serviceName == "" {
return fmt.Errorf("service_name argument is required")
}

privPEM := serviceSecret["private_key"]
block, _ := pem.Decode([]byte(privPEM))
if block == nil {
return fmt.Errorf("failed to parse private key PEM")
// Create JWT token using the common function
tokenString, err := createJWTToken(serviceName, privKey)
if err != nil {
return err
}

// Parse the private key
privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
// Output the token
fmt.Printf("JWT Token:\n%s\n", tokenString)

return nil
},
}

var pingCmd = &cli.Command{
Name: "ping",
Usage: "Ping the /pdp/ping endpoint of a PDP service",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service-url",
Usage: "URL of the PDP service",
Required: true,
},
&cli.StringFlag{
Name: "service-name",
Usage: "Service Name to include in the JWT token",
},
},
Action: func(cctx *cli.Context) error {
serviceURL := cctx.String("service-url")
serviceName := cctx.String("service-name")

if serviceName == "" {
return fmt.Errorf("either --jwt-token or --service-name must be provided")
}
privKey, err := loadPrivateKey()
if err != nil {
return fmt.Errorf("failed to parse private key: %v", err)
return err
}
ecdsaPrivKey, ok := privKey.(*ecdsa.PrivateKey)
if !ok {
return fmt.Errorf("private key is not ECDSA")
var errCreateToken error
jwtToken, errCreateToken := createJWTToken(serviceName, privKey)
if errCreateToken != nil {
return errCreateToken
}

// Get the service ID
serviceID := cctx.String("service-id")
if serviceID == "" {
return fmt.Errorf("service-id is required")
}
// Append /pdp/ping to the service URL
pingURL := serviceURL + "/pdp/ping"

// Create JWT claims
claims := jwt.MapClaims{
"service_id": serviceID,
"exp": time.Now().Add(time.Hour * 24).Unix(),
// Create the GET request
req, err := http.NewRequest("GET", pingURL, nil)
if err != nil {
return fmt.Errorf("failed to create request: %v", err)
}
req.Header.Set("Authorization", "Bearer "+jwtToken)

// Create the token
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)

// Sign the token
tokenString, err := token.SignedString(ecdsaPrivKey)
// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to sign token: %v", err)
return fmt.Errorf("failed to send request: %v", err)
}
defer resp.Body.Close()

// Output the token
fmt.Printf("JWT Token:\n%s\n", tokenString)
// Check the response
if resp.StatusCode == http.StatusOK {
fmt.Println("Ping successful: Service is reachable and JWT token is valid.")
} else {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("ping failed with status code %d: %s", resp.StatusCode, string(body))
}

return nil
},
}

func createJWTToken(serviceName string, privateKey *ecdsa.PrivateKey) (string, error) {
// Create JWT claims
claims := jwt.MapClaims{
"service_name": serviceName,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}

// Create the token
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)

// Sign the token
tokenString, err := token.SignedString(privateKey)
if err != nil {
return "", fmt.Errorf("failed to sign token: %v", err)
}

return tokenString, nil
}

func loadPrivateKey() (*ecdsa.PrivateKey, error) {
file, err := os.Open("pdpservice.json")
if err != nil {
return nil, fmt.Errorf("failed to open pdpservice.json: %v", err)
}
defer file.Close()
var serviceSecret map[string]string
decoder := json.NewDecoder(file)
if err := decoder.Decode(&serviceSecret); err != nil {
return nil, fmt.Errorf("failed to read pdpservice.json: %v", err)
}

privPEM := serviceSecret["private_key"]
block, _ := pem.Decode([]byte(privPEM))
if block == nil {
return nil, fmt.Errorf("failed to parse private key PEM")
}

// Parse the private key
privKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %v", err)
}
ecdsaPrivKey, ok := privKey.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("private key is not ECDSA")
}

return ecdsaPrivKey, nil
}

var piecePrepareCmd = &cli.Command{
Name: "prepare-piece",
Usage: "Compute the PieceCID of a file",
Expand Down
7 changes: 3 additions & 4 deletions pdp/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,16 @@ func (p *PDPService) verifyJWTToken(r *http.Request) (int64, error) {
}

// Extract service_id from claims
serviceIDFloat, ok := claims["service_id"].(float64)
serviceID, ok := claims["service_name"]
if !ok {
return nil, fmt.Errorf("service_id not found in token claims")
return nil, fmt.Errorf("missing service_name claim")
}
serviceID = int64(serviceIDFloat)

// Query the database for the public key using serviceID
var pubKeyBytes []byte
ctx := r.Context()
err := p.db.QueryRow(ctx, `
SELECT pubkey FROM pdp_services WHERE id=?
SELECT pubkey FROM pdp_services WHERE service_label=$1
`, serviceID).Scan(&pubKeyBytes)
if err != nil {
return nil, fmt.Errorf("failed to retrieve public key for service_id %d: %v", serviceID, err)
Expand Down
16 changes: 16 additions & 0 deletions pdp/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func Routes(r *chi.Mux, p *PDPService) {
})
})

r.Get(path.Join(PDPRoutePath, "/ping"), p.handlePing)

// Routes for piece storage and retrieval
// POST /pdp/piece
r.Post(path.Join(PDPRoutePath, "/piece"), p.handlePiecePost)
Expand All @@ -83,6 +85,20 @@ func Routes(r *chi.Mux, p *PDPService) {

// Handler functions

func (p *PDPService) handlePing(w http.ResponseWriter, r *http.Request) {
// Verify that the request is authorized using ECDSA JWT
_, err := p.verifyJWTToken(r)
if err != nil {
http.Error(w, "Unauthorized: "+err.Error(), http.StatusUnauthorized)
return
}

// Return 200 OK
w.WriteHeader(http.StatusOK)
}

// TODO STUFF BELOW IS JUST A SKELETON

func (p *PDPService) handleCreateProofSet(w http.ResponseWriter, r *http.Request) {
// Spec snippet:
// ### POST /proof-sets
Expand Down

0 comments on commit 07a5b22

Please sign in to comment.