From 3d1a1aa459ed4ca003d966fe18f6e049b99b68c6 Mon Sep 17 00:00:00 2001 From: Balint Gabor Date: Mon, 9 Apr 2018 23:52:13 +0200 Subject: [PATCH] cmd/swarm, swarm/api: Swarm command line api to upload encrypted files --- cmd/swarm/main.go | 11 ++++++++++- cmd/swarm/upload.go | 14 +++++++++++--- cmd/swarm/upload_test.go | 25 +++++++++++++++++++++---- swarm/api/client/client.go | 21 +++++++++++++++------ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go index 057b032ce0..f4ff0bb731 100644 --- a/cmd/swarm/main.go +++ b/cmd/swarm/main.go @@ -218,12 +218,21 @@ The output of this command is supposed to be machine-readable. `, }, { - Action: upload, + Action: nonEncryptedUpload, Name: "up", Usage: "upload a file or directory to swarm using the HTTP API", ArgsUsage: " ", Description: ` "upload a file or directory to swarm using the HTTP API and prints the root hash", +`, + }, + { + Action: encryptedUpload, + Name: "encrypted-up", + Usage: "Upload a file or directory with encryption to swarm using the HTTP API. NOTE: Currently the reference for the uploaded content is non-deterministic, so you will receive different references if you upload it twice.", + ArgsUsage: " ", + Description: ` +"upload a file or directory to swarm using the HTTP API and prints the root hash", `, }, { diff --git a/cmd/swarm/upload.go b/cmd/swarm/upload.go index 9f4c525bb9..058f93956d 100644 --- a/cmd/swarm/upload.go +++ b/cmd/swarm/upload.go @@ -35,7 +35,15 @@ import ( "gopkg.in/urfave/cli.v1" ) -func upload(ctx *cli.Context) { +func encryptedUpload(ctx *cli.Context) { + upload(ctx, true) +} + +func nonEncryptedUpload(ctx *cli.Context) { + upload(ctx, false) +} + +func upload(ctx *cli.Context, toEncrypt bool) { args := ctx.Args() var ( @@ -97,7 +105,7 @@ func upload(ctx *cli.Context) { if !recursive { return "", errors.New("Argument is a directory and recursive upload is disabled") } - return client.UploadDirectory(file, defaultPath, "") + return client.UploadDirectory(file, defaultPath, "", toEncrypt) } } else { doUpload = func() (string, error) { @@ -110,7 +118,7 @@ func upload(ctx *cli.Context) { mimeType = detectMimeType(file) } f.ContentType = mimeType - return client.Upload(f, "") + return client.Upload(f, "", toEncrypt) } } hash, err := doUpload() diff --git a/cmd/swarm/upload_test.go b/cmd/swarm/upload_test.go index 2bb601bdcb..f2ee999198 100644 --- a/cmd/swarm/upload_test.go +++ b/cmd/swarm/upload_test.go @@ -17,6 +17,7 @@ package main import ( + "fmt" "io" "io/ioutil" "net/http" @@ -29,6 +30,16 @@ import ( // TestCLISwarmUp tests that running 'swarm up' makes the resulting file // available from all nodes via the HTTP API func TestCLISwarmUp(t *testing.T) { + testCLISwarmUp(false, t) +} + +// TestCLISwarmUpEncrypted tests that running 'swarm encrypted-up' makes the resulting file +// available from all nodes via the HTTP API +func TestCLISwarmUpEncrypted(t *testing.T) { + testCLISwarmUp(true, t) +} + +func testCLISwarmUp(toEncrypt bool, t *testing.T) { log.Info("starting 3 node cluster") cluster := newTestCluster(t, 3) defer cluster.Shutdown() @@ -48,10 +59,16 @@ func TestCLISwarmUp(t *testing.T) { t.Fatal(err) } - // upload the file with 'swarm up' and expect a hash - log.Info("uploading file with 'swarm up'") - up := runSwarm(t, "--bzzapi", cluster.Nodes[0].URL, "up", tmp.Name()) - _, matches := up.ExpectRegexp(`[a-f\d]{64}`) + cmd := "up" + hashRegexp := `[a-f\d]{64}` + if toEncrypt { + cmd = "encrypted-up" + hashRegexp = `[a-f\d]{128}` + } + // upload the file with 'swarm up' or 'swarm encrypted-up' and expect a hash + log.Info(fmt.Sprintf("uploading file with '%s'", cmd)) + up := runSwarm(t, "--bzzapi", cluster.Nodes[0].URL, cmd, tmp.Name()) + _, matches := up.ExpectRegexp(hashRegexp) up.ExpectExit() hash := matches[0] log.Info("file uploaded", "hash", hash) diff --git a/swarm/api/client/client.go b/swarm/api/client/client.go index 8165d52d7e..31ba3de510 100644 --- a/swarm/api/client/client.go +++ b/swarm/api/client/client.go @@ -125,11 +125,11 @@ func Open(path string) (*File, error) { // (if the manifest argument is non-empty) or creates a new manifest containing // the file, returning the resulting manifest hash (the file will then be // available at bzz://) -func (c *Client) Upload(file *File, manifest string) (string, error) { +func (c *Client) Upload(file *File, manifest string, toEncrypt bool) (string, error) { if file.Size <= 0 { return "", errors.New("file size must be greater than zero") } - return c.TarUpload(manifest, &FileUploader{file}) + return c.TarUpload(manifest, &FileUploader{file}, toEncrypt) } // Download downloads a file with the given path from the swarm manifest with @@ -159,14 +159,14 @@ func (c *Client) Download(hash, path string) (*File, error) { // directory will then be available at bzz://path/to/file), with // the file specified in defaultPath being uploaded to the root of the manifest // (i.e. bzz://) -func (c *Client) UploadDirectory(dir, defaultPath, manifest string) (string, error) { +func (c *Client) UploadDirectory(dir, defaultPath, manifest string, toEncrypt bool) (string, error) { stat, err := os.Stat(dir) if err != nil { return "", err } else if !stat.IsDir() { return "", fmt.Errorf("not a directory: %s", dir) } - return c.TarUpload(manifest, &DirectoryUploader{dir, defaultPath}) + return c.TarUpload(manifest, &DirectoryUploader{dir, defaultPath}, toEncrypt) } // DownloadDirectory downloads the files contained in a swarm manifest under @@ -350,10 +350,19 @@ type UploadFn func(file *File) error // TarUpload uses the given Uploader to upload files to swarm as a tar stream, // returning the resulting manifest hash -func (c *Client) TarUpload(hash string, uploader Uploader) (string, error) { +func (c *Client) TarUpload(hash string, uploader Uploader, toEncrypt bool) (string, error) { reqR, reqW := io.Pipe() defer reqR.Close() - req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+hash, reqR) + addr := hash + + // If there is a hash already (a manifest), then that manifest will determine if the upload has + // to be encrypted or not. If there is no manifest then the toEncrypt parameter decides if + // there is encryption or not. + if hash == "" && toEncrypt { + // This is the built-in address for the encrypted upload endpoint + addr = "encrypt" + } + req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+addr, reqR) if err != nil { return "", err }