diff --git a/Peer ID.go b/Peer ID.go index 2a057c2..e2e7fa2 100644 --- a/Peer ID.go +++ b/Peer ID.go @@ -267,3 +267,16 @@ func unregisterPeerMonitor(channel chan<- *PeerInfo) { } } } + +// DeleteAccount deletes the account +func DeleteAccount() { + // delete the blockchain + UserBlockchain.DeleteBlockchain() + + // delete the warehouse + UserWarehouse.DeleteWarehouse() + + // delete the private key + config.PrivateKey = "" + saveConfig() +} diff --git a/blockchain/Blockchain.go b/blockchain/Blockchain.go index b44a072..da3dafe 100644 --- a/blockchain/Blockchain.go +++ b/blockchain/Blockchain.go @@ -328,3 +328,18 @@ func (blockchain *Blockchain) Read(number uint64) (decoded *BlockDecoded, status return decoded, StatusOK, nil } + +// DeleteBlockchain deletes the entire blockchain +func (blockchain *Blockchain) DeleteBlockchain() (status int, err error) { + blockchain.Lock() + defer blockchain.Unlock() + + for n := uint64(0); n < blockchain.height; n++ { + blockchain.database.Delete(blockNumberToKey(n)) + } + + // update the blockchain header in the database, reset height, increase version + blockchain.headerWrite(0, blockchain.version+1) + + return StatusOK, nil +} diff --git a/warehouse/Warehouse.go b/warehouse/Warehouse.go index e5b765f..5de5d5f 100644 --- a/warehouse/Warehouse.go +++ b/warehouse/Warehouse.go @@ -68,6 +68,15 @@ func (wh *Warehouse) FileExists(hash string) (path string, fileInfo os.FileInfo, return "", nil, false } +// DeleteWarehouse deletes all files in the warehouse +func (wh *Warehouse) DeleteWarehouse() (err error) { + return wh.IterateFiles(func(Hash []byte, Size int64) (Continue bool) { + wh.DeleteFile(Hash) + + return true + }) +} + // ---- hash functions ---- func ValidateHash(hash []byte) (hashA string, err error) { @@ -96,3 +105,68 @@ func buildPath(storagePath, hash string) (directory string, filename string) { return newPath, filename } + +// IterateFiles iterates through all the files and calls the callback +func (wh *Warehouse) IterateFiles(Callback func(Hash []byte, Size int64) (Continue bool)) (err error) { + // list all directories in the local Storage folder. We have to walk 2 levels down to see the actual files. + files, err := ioutil.ReadDir(wh.Directory) + if err != nil { + return err + } + + for _, file := range files { + name1 := file.Name() + _, err = hex.DecodeString(name1) + + // we are only looking for directories. Name has to be "XXXX" hex chars only. + if !file.IsDir() || len(name1) != 4 || err != nil { + continue + } + + // iterate through the next level + files1, err := ioutil.ReadDir(filepath.Join(wh.Directory, name1)) + if err != nil { + return err + } + + for _, file2 := range files1 { + name2 := file2.Name() + _, err = hex.DecodeString(name2) + + // we are only looking for directories. Name has to be "XXXX" hex chars only. + if !file2.IsDir() || len(name2) != 4 || err != nil { + continue + } + + // iterate through the next level + files2, err := ioutil.ReadDir(filepath.Join(wh.Directory, name1, name2)) + if err != nil { + return err + } + + for _, file3 := range files2 { + name3 := file3.Name() + _, err = hex.DecodeString(name3) + + // finally we are only looking for files + if file3.IsDir() || len(name3) != hashSize*2-8 || err != nil { + continue + } + + size := file3.Size() + + hash, err := hex.DecodeString(name1 + name2 + name3) + if err != nil { + return err + } + + // found the hash! forward + if !Callback(hash, size) { + return nil + } + } + } + } + + return nil +} diff --git a/webapi/API.go b/webapi/API.go index 2291605..2e9adf6 100644 --- a/webapi/API.go +++ b/webapi/API.go @@ -41,7 +41,8 @@ func Start(ListenAddresses []string, UseSSL bool, CertificateFile, CertificateKe Router.HandleFunc("/test", apiTest).Methods("GET") Router.HandleFunc("/status", apiStatus).Methods("GET") - Router.HandleFunc("/peer/self", apiPeerSelf).Methods("GET") + Router.HandleFunc("/account/info", apiAccountInfo).Methods("GET") + Router.HandleFunc("/account/delete", apiAccountDelete).Methods("GET") Router.HandleFunc("/blockchain/header", apiBlockchainHeaderFunc).Methods("GET") Router.HandleFunc("/blockchain/append", apiBlockchainAppend).Methods("POST") Router.HandleFunc("/blockchain/read", apiBlockchainRead).Methods("GET") diff --git a/webapi/Status.go b/webapi/Status.go index 6bba17d..50b3b30 100644 --- a/webapi/Status.go +++ b/webapi/Status.go @@ -9,6 +9,7 @@ package webapi import ( "encoding/hex" "net/http" + "strconv" "github.com/PeernetOfficial/core" ) @@ -50,11 +51,11 @@ type apiResponsePeerSelf struct { } /* -apiPeerSelf provides information about the self peer details -Request: GET /peer/self +apiAccountInfo provides information about the current account. +Request: GET /account/info Result: 200 with JSON structure apiResponsePeerSelf */ -func apiPeerSelf(w http.ResponseWriter, r *http.Request) { +func apiAccountInfo(w http.ResponseWriter, r *http.Request) { response := apiResponsePeerSelf{} response.NodeID = hex.EncodeToString(core.SelfNodeID()) @@ -63,3 +64,21 @@ func apiPeerSelf(w http.ResponseWriter, r *http.Request) { EncodeJSON(w, r, response) } + +/* +apiAccountDelete deletes the current account. The confirm parameter must include the user's choice. +Request: GET /account/delete?confirm=[0 or 1] +Result: 204 if the user choses not to delete the account + 200 if successfully deleted +*/ +func apiAccountDelete(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + if confirm, _ := strconv.ParseBool(r.Form.Get("confirm")); !confirm { + w.WriteHeader(http.StatusNoContent) + return + } + + core.DeleteAccount() + + w.WriteHeader(http.StatusOK) +} diff --git a/webapi/readme.md b/webapi/readme.md index 0204fd7..1d22fd7 100644 --- a/webapi/readme.md +++ b/webapi/readme.md @@ -6,10 +6,9 @@ It can be used by local client software to connect to the Peernet network and us ## Use Considerations (when not to use it) -Do not expose this API to the internet. The web API uses the core library which uses the user's public and private key to connect to the network. -It shall only run on a loopback IP such as `127.0.0.1` or `::1`. Special HTTP headers (including the Access-Control headers) are intentionally not set. +Do not expose this API to the internet or local network. The API is unauthenticated and provides direct access to the users blockchain. It also provide sensitive actions such as deleting the account (including the private key). An attacker could abuse the API to add any local file to the user's blockchain and then read it. -The API is unauthenticated and provides direct access to the users blockchain. +The API shall only run on a loopback IP such as `127.0.0.1` or `::1`. Special HTTP headers (including the Access-Control headers) are intentionally not set. ## Deployment @@ -27,8 +26,10 @@ webapi.Router.HandleFunc("/newfunction", newFunction).Methods("GET") These are the functions provided by the API: ``` -/status Provides current connectivity status to the network -/peer/self Provides information about the self peer details +/status Provide current connectivity status to the network + +/account/info Information about the current account +/account/delete Delete account /blockchain/header Header of the blockchain /blockchain/append Append a block to the blockchain @@ -89,12 +90,14 @@ type apiResponseStatus struct { } ``` -### Self Information +## Account API + +### Information This function returns information about the current peer. ``` -Request: GET /peer/self +Request: GET /account/info Response: 200 with JSON structure apiResponsePeerSelf ``` @@ -107,6 +110,18 @@ type apiResponsePeerSelf struct { } ``` +### Delete + +This deletes the account. This action is irreversible. After deleting the account, the backend shall no longer be used. + +Note that it currently does not send a termination message to other peers. As a result, other peers may retain data or metadata. + +``` +Request: GET /account/delete?confirm=[0 or 1] +Result: 204 if the user choses not to delete the account + 200 if successfully deleted +``` + ## Blockchain Functions Common status codes returned by various endpoints in the `blockchain` package: