From ddbf4935b9b9adffa5f2939e56cb49cbd5139dd1 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Sun, 10 May 2020 17:00:46 -0600 Subject: Update post to return hash and password --- backend/api/routes.go | 13 ++++++++++++- backend/db/db.go | 17 +++++++++-------- backend/db/schemas.go | 11 ++++++----- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/backend/api/routes.go b/backend/api/routes.go index a65c886..3b08781 100644 --- a/backend/api/routes.go +++ b/backend/api/routes.go @@ -23,6 +23,7 @@ func insertFunc(w http.ResponseWriter, r *http.Request) { expiry := r.FormValue("expiry") content := r.FormValue("content") title := r.FormValue("title") + password := r.FormValue("password") // get ip ip := getIP(r) @@ -30,11 +31,21 @@ func insertFunc(w http.ResponseWriter, r *http.Request) { log.Infof("got content '%s' and ip '%s'", content, ip) // insert content - err := db.New(ip, content, expiry, title) + paste, err := db.New(ip, content, expiry, title, password) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "got err: %s", err.Error()) } + + // if successful return paste hash + w.Header().Set("Content-Type", "application/json") + pasteMap := map[string]interface{}{ + "hash": paste.Hash, + "password": paste.Password, + } + + jsonData, _ := json.Marshal(pasteMap) + fmt.Fprintf(w, "%+v", string(jsonData)) } func getHashFunc(w http.ResponseWriter, r *http.Request) { diff --git a/backend/db/db.go b/backend/db/db.go index 4451f34..d40de87 100644 --- a/backend/db/db.go +++ b/backend/db/db.go @@ -28,7 +28,7 @@ const TitleLimit = 100 const ContentLimit = 100000 // creates a new paste with title, content and hash -func New(ip, content, expiry, title string) error { +func New(ip, content, expiry, title, password string) (Paste, error) { // generate hash from ip hash := hashing.GenerateURI(ip) @@ -42,14 +42,15 @@ func New(ip, content, expiry, title string) error { } // if any errors were found if errs != "" { - return fmt.Errorf(errs) + return Paste{}, fmt.Errorf(errs) } // create new struct new := Paste{ - Hash: hash, - Content: content, - Title: title, + Hash: hash, + Content: content, + Title: title, + Password: password, } // check if expiry @@ -58,12 +59,12 @@ func New(ip, content, expiry, title string) error { // if time format not current if err != nil { - return err + return Paste{}, err } // time is in the past if time.Now().After(t) { - return fmt.Errorf("time %s is in the past", t.String()) + return Paste{}, fmt.Errorf("time %s is in the past", t.String()) } new.Expiry = t @@ -76,7 +77,7 @@ func New(ip, content, expiry, title string) error { // insert struct log.Infof("create new paste with hash %s", hash) insertErr := insert(new) - return insertErr + return new, insertErr } // lookup diff --git a/backend/db/schemas.go b/backend/db/schemas.go index 62c5a11..4c73f82 100644 --- a/backend/db/schemas.go +++ b/backend/db/schemas.go @@ -8,9 +8,10 @@ import ( // Paste represents a single paste type Paste struct { - ID bson.ObjectId `bson:"_id,omitempty"` - Hash string - Content string - Expiry time.Time `bson:"expiry"` - Title string + ID bson.ObjectId `bson:"_id,omitempty"` + Hash string + Content string + Expiry time.Time `bson:"expiry"` + Title string + Password string } -- cgit v1.2.3 From 01d3631b893b09ec4c5f9daade727e8f88aa8d22 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Sun, 10 May 2020 17:32:31 -0600 Subject: Add password hashing --- backend/api/routes.go | 8 ++++---- backend/db/db.go | 45 ++++++++++++++++++++++++++++++--------------- backend/go.mod | 1 + backend/hashing/hash.go | 6 ++++++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/backend/api/routes.go b/backend/api/routes.go index 3b08781..d578632 100644 --- a/backend/api/routes.go +++ b/backend/api/routes.go @@ -31,7 +31,7 @@ func insertFunc(w http.ResponseWriter, r *http.Request) { log.Infof("got content '%s' and ip '%s'", content, ip) // insert content - paste, err := db.New(ip, content, expiry, title, password) + hash, err := db.New(ip, content, expiry, title, password) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "got err: %s", err.Error()) @@ -40,8 +40,7 @@ func insertFunc(w http.ResponseWriter, r *http.Request) { // if successful return paste hash w.Header().Set("Content-Type", "application/json") pasteMap := map[string]interface{}{ - "hash": paste.Hash, - "password": paste.Password, + "hash": hash, } jsonData, _ := json.Marshal(pasteMap) @@ -59,10 +58,11 @@ func getHashFunc(w http.ResponseWriter, r *http.Request) { return } - // otherwise, return paste content and current time + // otherwise, return paste content, title, and current time w.Header().Set("Content-Type", "application/json") pasteMap := map[string]interface{}{ "timestamp": time.Now(), + "title": paste.Title, "content": paste.Content, } diff --git a/backend/db/db.go b/backend/db/db.go index d40de87..9bfe55a 100644 --- a/backend/db/db.go +++ b/backend/db/db.go @@ -27,22 +27,21 @@ func init() { const TitleLimit = 100 const ContentLimit = 100000 -// creates a new paste with title, content and hash -func New(ip, content, expiry, title, password string) (Paste, error) { +// creates a new paste with title, content and hash, returns the hash of the created paste +func New(ip, content, expiry, title, password string) (string, error) { // generate hash from ip hash := hashing.GenerateURI(ip) // check for size of title and content - errs := "" - if len(title) > TitleLimit { - errs += fmt.Sprintf("title is longer than character limit of %d\n", TitleLimit) - } - if len(content) > ContentLimit { - errs += fmt.Sprintf("content is longer than character limit of %d\n", ContentLimit) + errs := checkLengths(title, content) + if errs != nil { + return "", errs } - // if any errors were found - if errs != "" { - return Paste{}, fmt.Errorf(errs) + + // hash given password + hashedPass, err := hashing.HashPassword(password) + if err != nil { + return "", fmt.Errorf("could not hash password: %s", err.Error()) } // create new struct @@ -50,7 +49,7 @@ func New(ip, content, expiry, title, password string) (Paste, error) { Hash: hash, Content: content, Title: title, - Password: password, + Password: hashedPass, } // check if expiry @@ -59,12 +58,12 @@ func New(ip, content, expiry, title, password string) (Paste, error) { // if time format not current if err != nil { - return Paste{}, err + return "", err } // time is in the past if time.Now().After(t) { - return Paste{}, fmt.Errorf("time %s is in the past", t.String()) + return "", fmt.Errorf("time %s is in the past", t.String()) } new.Expiry = t @@ -77,7 +76,23 @@ func New(ip, content, expiry, title, password string) (Paste, error) { // insert struct log.Infof("create new paste with hash %s", hash) insertErr := insert(new) - return new, insertErr + return hash, insertErr +} + +func checkLengths(title string, content string) error { + errs := "" + if len(title) > TitleLimit { + errs += fmt.Sprintf("title is longer than character limit of %d\n", TitleLimit) + } + if len(content) > ContentLimit { + errs += fmt.Sprintf("content is longer than character limit of %d\n", ContentLimit) + } + // if any errors were found + if errs != "" { + return fmt.Errorf(errs) + } + + return nil } // lookup diff --git a/backend/go.mod b/backend/go.mod index fc4b4dd..0832637 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -8,6 +8,7 @@ require ( github.com/joho/godotenv v1.3.0 github.com/kr/pretty v0.2.0 // indirect github.com/sirupsen/logrus v1.6.0 + golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/backend/hashing/hash.go b/backend/hashing/hash.go index 400659e..93a9cf9 100644 --- a/backend/hashing/hash.go +++ b/backend/hashing/hash.go @@ -3,6 +3,7 @@ package hashing import ( "crypto/md5" "encoding/hex" + "golang.org/x/crypto/bcrypt" "math/big" "time" ) @@ -23,4 +24,9 @@ func hashString(text string) string { bi := big.NewInt(0) bi.SetString(hexStr, 16) return bi.Text(62) +} + +func HashPassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hashedPassword), err } \ No newline at end of file -- cgit v1.2.3 From 86cb3213840403053aa9336dfdc5c947807c73d9 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Sun, 10 May 2020 18:09:49 -0600 Subject: Add auth check to get --- backend/api/routes.go | 12 ++++++++++-- backend/cache/cache.go | 13 +++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/backend/api/routes.go b/backend/api/routes.go index d578632..f41505b 100644 --- a/backend/api/routes.go +++ b/backend/api/routes.go @@ -52,9 +52,16 @@ func getHashFunc(w http.ResponseWriter, r *http.Request) { paste, err := cache.C.Get(hash) // if hash was not found - if err != nil { + if err == cache.PasteNotFound { w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, "got err: %s", err.Error()) + fmt.Fprintf(w, "got err: %s", err) + return + } + + // if paste is password protected + if err == cache.UserUnauthorized { + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprintf(w, "got err: %s", err) return } @@ -64,6 +71,7 @@ func getHashFunc(w http.ResponseWriter, r *http.Request) { "timestamp": time.Now(), "title": paste.Title, "content": paste.Content, + "expiry": paste.Expiry, } jsonData, _ := json.Marshal(pasteMap) diff --git a/backend/cache/cache.go b/backend/cache/cache.go index bac7ea8..1a8a7a1 100644 --- a/backend/cache/cache.go +++ b/backend/cache/cache.go @@ -1,6 +1,7 @@ package cache import ( + "errors" "sync" "github.com/jackyzha0/ctrl-v/db" @@ -13,6 +14,9 @@ type Cache struct { var C *Cache +var PasteNotFound = errors.New("could not find a paste with that hash") +var UserUnauthorized = errors.New("paste is password protected") + func init() { C = &Cache{ m: map[string]db.Paste{}, @@ -32,6 +36,15 @@ func (c *Cache) Get(hash string) (db.Paste, error) { // if it doesnt, lookup from db p, err := db.Lookup(hash) + if err != nil { + return p, PasteNotFound + } + + // if there is a password + if p.Password != "" { + return db.Paste{}, UserUnauthorized + } + c.add(p) return p, err } -- cgit v1.2.3