From a742faac83e830bbe90c60a9d303484f7faf9d02 Mon Sep 17 00:00:00 2001 From: b5 Date: Tue, 11 Feb 2020 15:00:58 -0500 Subject: [PATCH] feat(remote): remote client signs HTTP feed and preview requests --- remote/peer_sync_client.go | 32 ++++++++++++++++++++++++++ remote/peer_sync_client_test.go | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/remote/peer_sync_client.go b/remote/peer_sync_client.go index b1bc0517a..21aa1a8c3 100644 --- a/remote/peer_sync_client.go +++ b/remote/peer_sync_client.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "strings" + "time" coreiface "github.com/ipfs/interface-go-ipfs-core" crypto "github.com/libp2p/go-libp2p-core/crypto" @@ -24,6 +25,7 @@ import ( "github.com/qri-io/qri/repo" "github.com/qri-io/qri/repo/profile" reporef "github.com/qri-io/qri/repo/ref" + "github.com/qri-io/qri/version" ) var ( @@ -500,6 +502,28 @@ func (c *PeerSyncClient) AddDataset(ctx context.Context, ref *reporef.DatasetRef return base.ReplaceRefIfMoreRecent(node.Repo, &prevRef, ref) } +func (c *PeerSyncClient) signHTTPRequest(req *http.Request) error { + pk := c.node.Repo.PrivateKey() + now := fmt.Sprintf("%d", nowFunc().In(time.UTC).Unix()) + + // TODO (b5) - we shouldn't be calculating profile IDs here + peerID, err := calcProfileID(pk) + if err != nil { + return err + } + + b64Sig, err := signString(pk, requestSigningString(now, peerID, req.URL.Path)) + if err != nil { + return err + } + + req.Header.Add("timestamp", now) + req.Header.Add("pid", peerID) + req.Header.Add("signature", b64Sig) + req.Header.Add("qri-version", version.String) + return nil +} + // Feeds fetches the first page of featured & recent feeds in one call func (c *PeerSyncClient) Feeds(ctx context.Context, remoteAddr string) (map[string][]dsref.VersionInfo, error) { if at := addressType(remoteAddr); at != "http" { @@ -512,6 +536,10 @@ func (c *PeerSyncClient) Feeds(ctx context.Context, remoteAddr string) (map[stri return nil, err } + if err := c.signHTTPRequest(req); err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) if err != nil { if strings.Contains(err.Error(), "no such host") { @@ -551,6 +579,10 @@ func (c *PeerSyncClient) Preview(ctx context.Context, ref dsref.Ref, remoteAddr return nil, err } + if err := c.signHTTPRequest(req); err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) if err != nil { if strings.Contains(err.Error(), "no such host") { diff --git a/remote/peer_sync_client_test.go b/remote/peer_sync_client_test.go index 34e0a851f..1e4933cdd 100644 --- a/remote/peer_sync_client_test.go +++ b/remote/peer_sync_client_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/qri-io/dataset" "github.com/qri-io/qfs" "github.com/qri-io/qfs/cafs" "github.com/qri-io/qri/config" @@ -83,6 +84,45 @@ func TestClientFeedsAndPreviews(t *testing.T) { if diff := cmp.Diff(expect, feeds); diff != "" { t.Errorf("feeds result mismatch (-want +got): \n%s", diff) } + + ds, err := cli.Preview(tr.Ctx, reporef.ConvertToDsref(worldBankRef), server.URL) + if err != nil { + t.Error(err) + } + + expectDs := &dataset.Dataset{ + Body: []interface{}{float64(100)}, + BodyPath: "/ipfs/QmWVxUKnBmbiXai1Wgu6SuMzyZwYRqjt5TXL8xxghN5hWL", + Commit: &dataset.Commit{ + Message: "created dataset", + Path: "/ipfs/QmW27MUFMSvPiE3FpmHhSeBZQEuYAppofudDCLvPXVfSLR", + Qri: "cm:0", + Signature: "XLjvPUsiTxtnhkFajlPosxBl+id/tZJB1RWe9BwPpyqg3toIx6qOkhZtXefDh58rX1L0Id1HU0RkVP8sEl0L54d9C4xv25Uzyv3mAvT9VNN5pzblni5TPvU0mHIbawN57hSiywUP3HQLk8VbjRPo6qjpL5DngwvWXe8mAxTPKWwbV9Zx47tJJWImxJC5vLFRUD1KrRarnhYnGRyGaUiOxssaOnzERw49pA/1dDuFCEWghMpARVgWheZCyHN7rVTs+xH8XOTi8/Zz05bKTlpstm57BcCUENKqJgIt7bjsSIh/gHEc+et1A/kO/DBi3vcoKsA1vZI6lFoJzOwlKRKahg==", + Title: "initial commit", + }, + Meta: &dataset.Meta{Qri: "md:0", Title: "World Bank Population"}, + Name: "world_bank_population", + Path: "/ipfs/QmVeWbw4DJQqWjKXohgTu5JdhVniLPiyb6z6m1duwvXdQe", + Peername: "A", + Qri: "ds:0", + Structure: &dataset.Structure{ + Checksum: "QmShoKqAQ98zKKgLrSDGKDCkmAf6Ts1pgk5qPCXkaeshej", + Depth: 1, + Entries: 1, + Format: "json", + Length: 5, + Qri: "st:0", + Schema: map[string]interface{}{"type": string("array")}, + }, + } + + // calling meta has the side-effect of allocating dataset.Meta.meta + // TODO (b5) - this is bad. we need a meta constructor + expectDs.Meta.Meta() + + if diff := cmp.Diff(expectDs, ds, cmp.AllowUnexported(dataset.Dataset{}, dataset.Meta{})); diff != "" { + t.Errorf("preview result mismatch (-want +got): \n%s", diff) + } } func newMemRepoTestNode(t *testing.T) *p2p.QriNode {