Skip to content

Commit

Permalink
#52: Expose encloud APIs HTTP endpoints. (#53)
Browse files Browse the repository at this point in the history
* #52: Expose encloud APIs HTTP endpoints.

* #52: Fix temp file name.
- Increase server request timeout.

* #50: Replace module name. (#51)

* #50: Replace module name.

* #50: Fix file permission and buffer size issue.

* #50: Add default storage path to store retrieved file.

* #50 changes to readme and go.mod

* #50: Print downloaded file path.

* #50 fixed comment

---------

Co-authored-by: Parth Shukla <parth@bond180.com>

* #52: Add dynamic path in test.

* * #50: Replace module name.

* #50: Fix file permission and buffer size issue.

* #50: Add default storage path to store retrieved file.

* #50 changes to readme and go.mod

* #50: Print downloaded file path.

* #50 fixed comment

---------

Co-authored-by: Parth Shukla <parth@bond180.com>

* #52: Expose encloud APIs HTTP endpoints.

* #52: Fix temp file name.
- Increase server request timeout.

* #52: Add dynamic path in test.

* * #50: Replace module name.

* #50: Fix file permission and buffer size issue.

* #50: Add default storage path to store retrieved file.

* #50 changes to readme and go.mod

* #50: Print downloaded file path.

* #50 fixed comment

---------

Co-authored-by: Parth Shukla <parth@bond180.com>

* #52 updated README and dependencies

---------

Co-authored-by: Parth Shukla <parth@bond180.com>
  • Loading branch information
vivekshah92 and parthbond180 authored Sep 18, 2023
1 parent b125e26 commit 09a2c7a
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 27 deletions.
24 changes: 4 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ clouds like Filecoin, don't offer privacy natively rendering them unuseful for s

**encloud solves this.**

encloud has two offerings
encloud has three offerings

- The encloud CLI enables users to easily on-board sensitive data to the Filecoin network.
- The encloud Desktop Application is a lightweight GUI built on top of the CLI and can be downloaded via the [encloud website](https://encloud.tech/).
- The [encloud CLI](cmd/cli/) enables users to easily on-board sensitive data to the Filecoin network.
- The [encloud Desktop Application](cmd/web/) is a lightweight GUI built on top of the CLI and can be downloaded via the [encloud website](https://encloud.tech/).
- [Readme](cmd/web/README.md)
- [How to guide](cmd/web/HOWTO.md)

- The [encloud API](cmd/api/) is a REST API that can be used to integrate encloud into your web applications.
# Encloud CLI

encloud lets users manage encryption keys and onboard their encrypted data to Filecoin
Expand All @@ -22,14 +22,6 @@ encloud lets users manage encryption keys and onboard their encrypted data to Fi
- Retrieve encrypted files from Filecoin and decrypt them
- Share encrypted files by transferring the DEK to a specified email

encloud currently uses the [**Estuary**](https://estuary.tech/) API to upload and retrieve data from Filecoin. This allows clients to interact with the
Filecoin network without needing to deal with crypto wallets, tokens or deal making with Filecoin storage providers as
Estuary manages this in the background.

We plan to add more flows to enable clients to control their deal making with specific Storage Providers.
To this end we want to integrate with Singularity and its deal preparation module to generate encrypted CAR files and make deals
with specific storage providers.

**Watch the encloud CLI demo in action!**

[![encloud CLI Demo](http://img.youtube.com/vi/R-j_533QZ08/0.jpg)](https://www.youtube.com/watch?v=R-j_533QZ08 "encloud CLI Demo")
Expand Down Expand Up @@ -104,14 +96,6 @@ go build -o $GOPATH/bin/encloud github.com/encloud-tech/encloud/cmd/cli

> encloud config -p `<CONFIG_YAML_PATH>`
## Future features

- Run bacalhau compute jobs on encrypted data natively via encloud
- Offering on-boarding and pinning alternatives to Estuary, e.x., Delta
- Deal Preparation module to generate encrypted CAR files for direct SP ingestion (Premium)
- Distributed key management for KEKs (Premium)
- Chunking for performant file uploads (Premium)

## Support

- Please file an issue to get help or report a bug.
Expand Down
310 changes: 310 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
package main

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"time"

"github.com/encloud-tech/encloud/pkg/api"
"github.com/encloud-tech/encloud/pkg/types"
thirdparty "github.com/encloud-tech/encloud/third_party"

"github.com/gorilla/mux"
)

func home(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Welcome to encloud API\n")
}

func KeyGenHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

cfg, err := api.Fetch()
if err != nil {
fmt.Print(err)
}

var response types.GenerateKeyPairResponse
keys, err := api.GenerateKeyPair(cfg.Stat.KekType)
if err != nil {
response = types.GenerateKeyPairResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
Data: types.Keys{},
}
} else {
response = types.GenerateKeyPairResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Keys generated successfully.",
Data: keys,
}
}

json.NewEncoder(w).Encode(response)
}

func KeysHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

var response types.ListKeysResponse
keys, err := api.ListKeys()
if err != nil {
response = types.ListKeysResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
Data: types.ListKeys{},
}
} else {
response = types.ListKeysResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Keys fetched successfully.",
Data: keys,
}
}

json.NewEncoder(w).Encode(response)
}

func ContentsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

r.ParseForm()
kek := ""
publicKey := r.FormValue("pubkey")
readPublicKeyFromPath := r.FormValue("readPubFromPath")

if readPublicKeyFromPath == "true" {
kek = thirdparty.ReadKeyFile(publicKey)
} else {
kek = publicKey
}

var response types.ListContentResponse
contents := api.List(kek)
response = types.ListContentResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Content fetched successfully.",
Data: contents,
}

json.NewEncoder(w).Encode(response)
}

func RetrieveHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

kek := ""
privateKey := ""

r.ParseForm()
pubkey := r.FormValue("pubkey")
privkey := r.FormValue("privkey")
readPublicKeyFromPath := r.FormValue("readPubFromPath")
readPrivateKeyFromPath := r.FormValue("readPrivFromPath")
retrievalFileStoragePath := r.FormValue("storage")
uuid := r.FormValue("uuid")

if readPublicKeyFromPath == "true" {
kek = thirdparty.ReadKeyFile(pubkey)
} else {
kek = pubkey
}

if readPrivateKeyFromPath == "true" {
privateKey = thirdparty.ReadKeyFile(privkey)
} else {
privateKey = privkey
}

var response types.RetrieveByUUIDContentResponse
fileMetaData, err := api.RetrieveByUUID(uuid, kek, privateKey, retrievalFileStoragePath)
if err != nil {
response = types.RetrieveByUUIDContentResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
Data: types.FileMetadata{},
}
} else {
response = types.RetrieveByUUIDContentResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Content fetched successfully.",
Data: fileMetaData,
}
}

json.NewEncoder(w).Encode(response)
}

func ShareHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

kek := ""
privateKey := ""

r.ParseForm()
pubkey := r.FormValue("pubkey")
privkey := r.FormValue("privkey")
readPublicKeyFromPath := r.FormValue("readPubFromPath")
readPrivateKeyFromPath := r.FormValue("readPrivFromPath")
email := r.FormValue("email")
uuid := r.FormValue("uuid")

if readPublicKeyFromPath == "true" {
kek = thirdparty.ReadKeyFile(pubkey)
} else {
kek = pubkey
}

if readPrivateKeyFromPath == "true" {
privateKey = thirdparty.ReadKeyFile(privkey)
} else {
privateKey = privkey
}

var response types.RetrieveByUUIDContentResponse
fileMetaData, err := api.Share(uuid, kek, privateKey, email)
if err != nil {
response = types.RetrieveByUUIDContentResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
Data: types.FileMetadata{},
}
} else {
response = types.RetrieveByUUIDContentResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Content shared successfully.",
Data: fileMetaData,
}
}

json.NewEncoder(w).Encode(response)
}

func SharedHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

r.ParseForm()
decryptedDekPath := r.FormValue("decryptedDekPath")
dekType := r.FormValue("dekType")
cid := r.FormValue("cid")
fileName := r.FormValue("fileName")
retrievalFileStoragePath := r.FormValue("retrievalFileStoragePath")

var response types.SharedResponse
err := api.RetrieveSharedContent(decryptedDekPath, dekType, cid, fileName, retrievalFileStoragePath)
if err != nil {
response = types.SharedResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
}
} else {
response = types.SharedResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Content retrieved successfully.",
}
}

json.NewEncoder(w).Encode(response)
}

func UploadHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)

cfg, err := api.Fetch()
if err != nil {
fmt.Print(err.Error())
}

kek := ""

file, handle, err := r.FormFile("file")
pubkey := r.Form.Get("pubkey")
readPublicKeyFromPath := r.Form.Get("readPubFromPath")
dekType := r.Form.Get("type")

if readPublicKeyFromPath == "true" {
kek = thirdparty.ReadKeyFile(pubkey)
} else {
kek = pubkey
}

// Create a temporary file.
tempFile, err := os.CreateTemp("", "*"+filepath.Ext(handle.Filename))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer tempFile.Close()

// Copy the content of the uploaded file to the temporary file.
_, err = io.Copy(tempFile, file)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

var response types.UploadContentResponse
uuid, err := api.Upload(tempFile.Name(), cfg.Stat.KekType, dekType, kek)
if err != nil {
response = types.UploadContentResponse{
Status: "fail",
StatusCode: http.StatusInternalServerError,
Message: err.Error(),
Data: types.Uuid{},
}
} else {
response = types.UploadContentResponse{
Status: "success",
StatusCode: http.StatusCreated,
Message: "Content uploaded successfully.",
Data: types.Uuid{Uuid: uuid},
}
}

json.NewEncoder(w).Encode(response)
}

func main() {
router := mux.NewRouter()
router.HandleFunc("/", home)
router.HandleFunc("/keygen", KeyGenHandler).Methods("GET")
router.HandleFunc("/upload", UploadHandler).Methods("POST")
router.HandleFunc("/keys", KeysHandler).Methods("GET")
router.HandleFunc("/contents", ContentsHandler).Methods("POST")
router.HandleFunc("/retrieve", RetrieveHandler).Methods("POST")
router.HandleFunc("/share", ShareHandler).Methods("POST")
router.HandleFunc("/shared", SharedHandler).Methods("POST")

srv := &http.Server{
Handler: router,
Addr: "127.0.0.1:9000",
// Good practice to enforce timeouts for servers you create!
WriteTimeout: 60 * time.Second,
ReadTimeout: 60 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
7 changes: 3 additions & 4 deletions cmd/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"

Expand All @@ -21,15 +20,15 @@ func ConfigCmd() *cobra.Command {
Long: `Update configurations for the application using a compatible yaml file`,
Run: func(cmd *cobra.Command, args []string) {
configFilePath, _ := cmd.Flags().GetString("path")
data, err := ioutil.ReadFile(configFilePath)
data, err := os.ReadFile(configFilePath)
if err != nil {
fmt.Fprintf(cmd.OutOrStderr(), err.Error())
fmt.Fprint(cmd.OutOrStderr(), err.Error())
os.Exit(-1)
}

var conf types.ConfYaml
if err := yaml.Unmarshal(data, &conf); err != nil {
fmt.Fprintf(cmd.OutOrStderr(), err.Error())
fmt.Fprint(cmd.OutOrStderr(), err.Error())
os.Exit(-1)
}

Expand Down
Loading

0 comments on commit 09a2c7a

Please sign in to comment.