Skip to content

Commit

Permalink
wip: ipns signed record on gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Nov 29, 2022
1 parent 8cca21a commit df28c43
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 6 deletions.
5 changes: 5 additions & 0 deletions core/coreapi/coreapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ func (api *CoreAPI) PubSub() coreiface.PubSubAPI {
return (*PubSubAPI)(api)
}

// Routing returns the RoutingAPI interface implementation backed by the go-ipfs node
func (api *CoreAPI) Routing() coreiface.RoutingAPI {
return (*RoutingAPI)(api)
}

// WithOptions returns api with global options applied
func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, error) {
settings := api.parentOpts // make sure to copy
Expand Down
70 changes: 70 additions & 0 deletions core/coreapi/routing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package coreapi

import (
"context"
"encoding/base64"
"errors"

"github.com/ipfs/go-path"
peer "github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
)

type RoutingAPI CoreAPI

func (r *RoutingAPI) Get(ctx context.Context, key string) ([]byte, error) {
dhtKey, err := ensureIPNSKey(key)
if err != nil {
return nil, err
}

ctx, cancel := context.WithCancel(ctx)
ctx, events := routing.RegisterForQueryEvents(ctx)

var getErr error

go func() {
defer cancel()
var val []byte
val, getErr = r.routing.GetValue(ctx, dhtKey)
if getErr != nil {
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
Type: routing.QueryError,
Extra: getErr.Error(),
})
} else {
routing.PublishQueryEvent(ctx, &routing.QueryEvent{
Type: routing.Value,
Extra: base64.StdEncoding.EncodeToString(val),
})
}
}()

for e := range events {
if e.Type == routing.Value {
return []byte(e.Extra), nil
}
}

if err := ctx.Err(); err != nil {
return nil, err
}

return nil, errors.New("key not found")
}

// Shamelessly (mostly) copied from commands/routing.go
func ensureIPNSKey(s string) (string, error) {
parts := path.SplitList(s)
if len(parts) != 3 ||
parts[0] != "" ||
parts[1] != "ipns" {
return "", errors.New("invalid key")
}

k, err := peer.Decode(parts[2])
if err != nil {
return "", err
}
return path.Join(append(parts[:2], string(k))), nil
}
4 changes: 4 additions & 0 deletions core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ type NodeAPI interface {
// Dag returns an implementation of Dag API
Dag() coreiface.APIDagService

// Routing returns an implementation of Routing API.
// Used for returning signed IPNS records, see IPIP-0328
Routing() coreiface.RoutingAPI

// ResolvePath resolves the path using Unixfs resolver
ResolvePath(context.Context, path.Path) (path.Resolved, error)
}
Expand Down
8 changes: 8 additions & 0 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,12 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
logger.Debugw("serving tar file", "path", contentPath)
i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger)
return
case "application/vnd.ipfs.ipns-record":
// TODO: i.handlePathResolution has been executed here, but we don't really need it. Should we check
// this beforehand?
logger.Debugw("serving ipns record", "path", contentPath)
i.serveIpnsRecord(r.Context(), w, r, contentPath, begin, logger)
return
default: // catch-all for unsuported application/vnd.*
err := fmt.Errorf("unsupported format %q", responseFormat)
webError(w, "failed respond with requested content type", err, http.StatusBadRequest)
Expand Down Expand Up @@ -866,6 +872,8 @@ func customResponseFormat(r *http.Request) (mediaType string, params map[string]
return "application/vnd.ipld.car", nil, nil
case "tar":
return "application/x-tar", nil, nil
case "ipns-record":
return "application/vnd.ipfs.ipns-record", nil, nil
}
}
// Browsers and other user agents will send Accept header with generic types like:
Expand Down
50 changes: 50 additions & 0 deletions core/corehttp/gateway_handler_ipns_record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package corehttp

import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"time"

path "github.com/ipfs/go-path"
ipath "github.com/ipfs/interface-go-ipfs-core/path"
"go.uber.org/zap"
)

func (i *gatewayHandler) serveIpnsRecord(ctx context.Context, w http.ResponseWriter, r *http.Request, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) {
if contentPath.Namespace() != "ipns" {
err := fmt.Errorf("%s is not an IPNS link", contentPath.String())
webError(w, err.Error(), err, http.StatusBadRequest)
return
}

key := contentPath.String()
key = strings.TrimSuffix(key, "/")
if strings.Count(key, "/") > 2 {
err := errors.New("cannot find ipns key for subpath")
webError(w, err.Error(), err, http.StatusBadRequest)
return
}

record, err := i.api.Routing().Get(ctx, key)
if err != nil {
webError(w, err.Error(), err, http.StatusInternalServerError)
return
}

// Set Content-Disposition
var name string
if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" {
name = urlFilename
} else {
name = path.SplitList(key)[2] + ".ipns-record"
}
setContentDispositionHeader(w, name, "attachment")

w.Header().Set("Content-Type", "application/vnd.ipfs.ipns-record")
w.Header().Set("X-Content-Type-Options", "nosniff")

_, _ = w.Write(record)
}
2 changes: 1 addition & 1 deletion docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ replace github.com/ipfs/kubo => ./../../..

require (
github.com/ipfs/go-ipfs-files v0.2.0
github.com/ipfs/interface-go-ipfs-core v0.7.0
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874
github.com/ipfs/kubo v0.14.0-rc1
github.com/libp2p/go-libp2p v0.23.4
github.com/multiformats/go-multiaddr v0.7.0
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/kubo-as-a-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -657,8 +657,8 @@ github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZ
github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs=
github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU=
github.com/ipfs/interface-go-ipfs-core v0.4.0/go.mod h1:UJBcU6iNennuI05amq3FQ7g0JHUkibHFAfhfUIy927o=
github.com/ipfs/interface-go-ipfs-core v0.7.0 h1:7tb+2upz8oCcjIyjo1atdMk+P+u7wPmI+GksBlLE8js=
github.com/ipfs/interface-go-ipfs-core v0.7.0/go.mod h1:lF27E/nnSPbylPqKVXGZghal2hzifs3MmjyiEjnc9FY=
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874 h1:55uGMOPDlAH5kAqjAjRZfvaBfUCWafY9uRzCKAq9Rag=
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874/go.mod h1:X/udt0qeqxXlgv69JQ8g38gWy4LrCyOuav6f7KDoJMo=
github.com/ipfs/tar-utils v0.0.2/go.mod h1:4qlnRWgTVljIMhSG2SqRYn66NT+3wrv/kZt9V+eqxDM=
github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk=
github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ require (
github.com/ipfs/go-unixfs v0.4.0
github.com/ipfs/go-unixfsnode v1.4.0
github.com/ipfs/go-verifcid v0.0.2
github.com/ipfs/interface-go-ipfs-core v0.7.0
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874
github.com/ipfs/tar-utils v0.0.2
github.com/ipld/go-car v0.4.0
github.com/ipld/go-car/v2 v2.4.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,8 @@ github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZ
github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs=
github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU=
github.com/ipfs/interface-go-ipfs-core v0.4.0/go.mod h1:UJBcU6iNennuI05amq3FQ7g0JHUkibHFAfhfUIy927o=
github.com/ipfs/interface-go-ipfs-core v0.7.0 h1:7tb+2upz8oCcjIyjo1atdMk+P+u7wPmI+GksBlLE8js=
github.com/ipfs/interface-go-ipfs-core v0.7.0/go.mod h1:lF27E/nnSPbylPqKVXGZghal2hzifs3MmjyiEjnc9FY=
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874 h1:55uGMOPDlAH5kAqjAjRZfvaBfUCWafY9uRzCKAq9Rag=
github.com/ipfs/interface-go-ipfs-core v0.7.1-0.20221114161843-63169fd5e874/go.mod h1:X/udt0qeqxXlgv69JQ8g38gWy4LrCyOuav6f7KDoJMo=
github.com/ipfs/tar-utils v0.0.2 h1:UNgHB4x/PPzbMkmJi+7EqC9LNMPDztOVSnx1HAqSNg4=
github.com/ipfs/tar-utils v0.0.2/go.mod h1:4qlnRWgTVljIMhSG2SqRYn66NT+3wrv/kZt9V+eqxDM=
github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk=
Expand Down

0 comments on commit df28c43

Please sign in to comment.