Make deleteTokens strings

This commit is contained in:
Daan Sprenkels 2019-09-21 21:03:31 +02:00
parent 1c926a4864
commit f45c132b41
3 changed files with 28 additions and 51 deletions

View File

@ -0,0 +1,3 @@
{{define "title"}}
Success - rushlink
{{end}}

View File

@ -0,0 +1 @@
{{.Request.Host}}/{{.Paste.Key}}?deleteToken={{.Paste.DeleteToken}}

View File

@ -28,7 +28,7 @@ type storedPaste struct {
State pasteState State pasteState
Content []byte Content []byte
Key string Key string
DeleteToken [16]byte DeleteToken string
TimeCreated time.Time TimeCreated time.Time
} }
@ -126,13 +126,11 @@ func viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPas
Bool bool Bool bool
String string String string
}{Bool: false} }{Bool: false}
deleteToken, err := getDeleteTokenFromRequest(r) deleteToken := getDeleteTokenFromRequest(r)
if err != nil { if deleteToken == "" {
canDelete.String = "invalid"
} else if deleteToken == nil {
canDelete.String = "undefined" canDelete.String = "undefined"
} else { } else {
if subtle.ConstantTimeCompare(deleteToken[:], storedPaste.DeleteToken[:]) == 1 { if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(storedPaste.DeleteToken)) == 1 {
canDelete.Bool = true canDelete.Bool = true
canDelete.String = "correct" canDelete.String = "correct"
} else { } else {
@ -162,7 +160,7 @@ func viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPas
} }
w.Write(storedPaste.Content) w.Write(storedPaste.Content)
case stateDeleted: case stateDeleted:
renderError(w, r, http.StatusGone, "key has been deleted") renderError(w, r, http.StatusGone, "paste has been deleted")
default: default:
log.Printf("error: invalid storedPaste.State (%v) for key '%v'\n", storedPaste.State, storedPaste.Key) log.Printf("error: invalid storedPaste.State (%v) for key '%v'\n", storedPaste.State, storedPaste.Key)
msg := fmt.Sprintf("internal server error: invalid storedPaste.State (%v\n)", storedPaste.State) msg := fmt.Sprintf("internal server error: invalid storedPaste.State (%v\n)", storedPaste.State)
@ -215,33 +213,16 @@ func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request) {
var storedPaste *storedPaste var storedPaste *storedPaste
if err := db.DB.Update(func(tx *bolt.Tx) error { if err := db.DB.Update(func(tx *bolt.Tx) error {
// Generate a new delete token for this paste // Generate a new delete token for this paste
deleteToken, err := generateDeleteToken() var err error
if err != nil { storedPaste, err = shortenURL(tx, userURL)
return errors.Wrap(err, "generating delete token")
}
sp, err := shortenURL(tx, userURL, deleteToken)
storedPaste = sp
return err return err
}); err != nil { }); err != nil {
log.Printf("error: %v\n", err) log.Printf("error: %v\n", err)
renderInternalServerError(w, r, err) renderInternalServerError(w, r, err)
return return
} }
data := map[string]interface{}{"Paste": storedPaste}
deleteToken := hex.EncodeToString(storedPaste.DeleteToken[:]) render(w, r, "newRedirectPasteSuccess", data)
saveRawurl := fmt.Sprintf("%v/%v?deleteToken=%v", r.Host, string(storedPaste.Key), deleteToken)
saveURL, err := r.URL.Parse(saveRawurl)
if err != nil {
err = errors.Wrap(err, "parsing url")
log.Printf("error: %v\n", err)
renderInternalServerError(w, r, err)
return
}
// TODO(dsprenkels) Put this into a template
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "%v\n", saveURL)
} }
// Delete a URL from the database // Delete a URL from the database
@ -250,11 +231,8 @@ func deletePasteHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
key := vars["key"] key := vars["key"]
deleteToken, err := getDeleteTokenFromRequest(r) deleteToken := getDeleteTokenFromRequest(r)
if err != nil { if deleteToken == "" {
renderError(w, r, http.StatusBadRequest, "invalid delete token")
return
} else if deleteToken == nil {
renderError(w, r, http.StatusBadRequest, "no delete token provided") renderError(w, r, http.StatusBadRequest, "no delete token provided")
return return
} }
@ -266,7 +244,7 @@ func deletePasteHandler(w http.ResponseWriter, r *http.Request) {
errorCode = http.StatusNotFound errorCode = http.StatusNotFound
return err return err
} }
if subtle.ConstantTimeCompare(deleteToken[:], paste.DeleteToken[:]) == 1 { if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(paste.DeleteToken)) == 1 {
// Replace the old paste with a new empty paste // Replace the old paste with a new empty paste
return savePaste(tx, key, storedPaste{ return savePaste(tx, key, storedPaste{
Key: paste.Key, Key: paste.Key,
@ -301,7 +279,7 @@ func getURL(tx *bolt.Tx, key string) (*storedPaste, error) {
// Add a new URL to the database // Add a new URL to the database
// //
// Returns the new ID if the url was successfully shortened // Returns the new ID if the url was successfully shortened
func shortenURL(tx *bolt.Tx, userURL *url.URL, deleteToken [16]byte) (*storedPaste, error) { func shortenURL(tx *bolt.Tx, userURL *url.URL) (*storedPaste, error) {
pastesBucket := tx.Bucket([]byte(db.BUCKET_PASTES)) pastesBucket := tx.Bucket([]byte(db.BUCKET_PASTES))
if pastesBucket == nil { if pastesBucket == nil {
return nil, errors.Errorf("bucket %v does not exist", db.BUCKET_PASTES) return nil, errors.Errorf("bucket %v does not exist", db.BUCKET_PASTES)
@ -337,6 +315,12 @@ func shortenURL(tx *bolt.Tx, userURL *url.URL, deleteToken [16]byte) (*storedPas
epoch++ epoch++
} }
// Also generate a deleteToken
deleteToken, err := generateDeleteToken()
if err != nil {
return nil, errors.Wrap(err, "generating delete token")
}
// Store the new key // Store the new key
storedPaste := storedPaste{ storedPaste := storedPaste{
Type: typeRedirect, Type: typeRedirect,
@ -403,26 +387,15 @@ func generateURLKey(epoch int) (string, error) {
return string(urlKey), nil return string(urlKey), nil
} }
func generateDeleteToken() ([16]byte, error) { func generateDeleteToken() (string, error) {
var deleteToken [16]byte var deleteToken [16]byte
_, err := rand.Read(deleteToken[:]) _, err := rand.Read(deleteToken[:])
if err != nil { if err != nil {
return deleteToken, err return "", err
} }
return deleteToken, nil return hex.EncodeToString(deleteToken[:]), nil
} }
func getDeleteTokenFromRequest(r *http.Request) (*[16]byte, error) { func getDeleteTokenFromRequest(r *http.Request) string {
deleteTokenQuery := r.URL.Query().Get("deleteToken") return r.URL.Query().Get("deleteToken")
if deleteTokenQuery == "" {
return nil, nil
}
var deleteToken [16]byte
n, err := hex.Decode(deleteToken[:], []byte(deleteTokenQuery))
if err != nil {
return nil, errors.Wrap(err, "decoding hex")
} else if n != 16 {
return nil, errors.Errorf("invalid deleteToken length (%v bytes)", n)
}
return &deleteToken, nil
} }