Show meta page immediately after create

This commit is contained in:
Daan Sprenkels 2019-12-17 15:33:29 +05:30
parent c46a26f8a2
commit 3d07acb222
4 changed files with 87 additions and 76 deletions

View File

@ -6,8 +6,8 @@
<pre>
<a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}</a>
---
{{if and (ne .Paste.State.String "deleted") .CanDelete.Bool}}
with delete token: <a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}</a>
{{if and (ne .Paste.State.String "deleted") .CanDeleteBool}}
with delete token: <a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}</a>
{{else -}}
with delete token: &lt;unknown&gt;
{{end -}}
@ -18,18 +18,11 @@ created: unknown
{{else -}}
created: {{.Paste.TimeCreated}}
{{end -}}
delete token: {{.CanDelete.String}}
delete token: {{.CanDelete}}
{{if and (ne .Paste.State.String "deleted") .CanDelete.Bool}}
{{if and (ne .Paste.State.String "deleted") .CanDeleteBool}}
```
# To delete this {{.Paste.Type}}, execute:
{{- /*
We have the option here to take the deleteToken from the user request or
from .Paste. Both are equivalent as long as .CanDelete is correct. We
use the .Request value, because leaking the deleteToken would be a more
dramatic vulnerability.
*/}}
curl --request "DELETE" "<a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}"</a>
curl --request "DELETE" "<a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}"</a>
```
</pre>
{{end}}

View File

@ -1,6 +1,6 @@
{{.Host}}/{{.Paste.Key}}{{.FileExt}}
---
{{if and (ne .Paste.State.String "deleted") .CanDelete.Bool}}
{{if and (ne .Paste.State.String "deleted") .CanDeleteBool}}
with delete token: {{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}
{{else -}}
with delete token: <unknown>
@ -11,17 +11,11 @@ created: unknown
{{else -}}
created: {{.Paste.TimeCreated}}
{{end -}}
delete token: {{.CanDelete.String}}
delete token: {{.CanDelete}}
{{if and (ne .Paste.State.String "deleted") .CanDelete.Bool}}
{{if and (ne .Paste.State.String "deleted") .CanDeleteBool}}
```
# To delete this {{.Paste.Type}}, execute:
{{- /*
We have the option here to take the deleteToken from the user request or
from .Paste. Both are equivalent as long as .CanDelete is correct. We
use the .Request value, because leaking the deleteToken would be a more
dramatic vulnerability.
*/ -}}
curl --request "DELETE" "{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}"
curl --request "DELETE" "{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}"
```
{{end}}

View File

@ -28,6 +28,31 @@ const (
const cookieDeleteToken = "owner_token"
type canDelete uint
const (
canDeleteUndef canDelete = iota
canDeleteYes
canDeleteNo
)
func (cd *canDelete) Bool() bool {
return *cd == canDeleteYes
}
func (cd *canDelete) String() string {
switch *cd {
case canDeleteUndef:
return "undefined"
case canDeleteYes:
return "correct"
case canDeleteNo:
return "invalid"
default:
panic("unreachable")
}
}
func (rl *rushlink) staticGetHandler(w http.ResponseWriter, r *http.Request) {
rl.renderStatic(w, r, mux.Vars(r)["path"])
}
@ -79,24 +104,22 @@ func (rl *rushlink) uploadFileGetHandler(w http.ResponseWriter, r *http.Request)
}
func (rl *rushlink) viewPasteHandler(w http.ResponseWriter, r *http.Request) {
rl.viewPasteHandlerInner(w, r, 0)
rl.viewPasteHandlerFlags(w, r, 0)
}
func (rl *rushlink) viewPasteHandlerNoRedirect(w http.ResponseWriter, r *http.Request) {
rl.viewPasteHandlerInner(w, r, viewNoRedirect)
rl.viewPasteHandlerFlags(w, r, viewNoRedirect)
}
func (rl *rushlink) viewPasteHandlerMeta(w http.ResponseWriter, r *http.Request) {
rl.viewPasteHandlerInner(w, r, viewShowMeta)
rl.viewPasteHandlerFlags(w, r, viewShowMeta)
}
func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPaste) {
func (rl *rushlink) viewPasteHandlerFlags(w http.ResponseWriter, r *http.Request, flags viewPaste) {
vars := mux.Vars(r)
key := vars["key"]
var p *db.Paste
var fuID *uuid.UUID
var fu *db.FileUpload
var fileExt string
if err := rl.db.Bolt.View(func(tx *bolt.Tx) error {
var err error
p, err = db.GetPaste(tx, key)
@ -106,12 +129,10 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
if p != nil && p.Type == db.PasteTypeFileUpload {
var id uuid.UUID
copy(id[:], p.Content)
fuID = &id
fu, err = db.GetFileUpload(tx, id)
if err != nil {
return err
}
fileExt = fu.Ext()
}
return nil
}); err != nil {
@ -123,30 +144,12 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
return
}
if flags&viewShowMeta != 0 {
canDelete := struct {
Bool bool
String string
}{Bool: false}
deleteToken := getDeleteTokenFromRequest(r)
if deleteToken == "" {
canDelete.String = "undefined"
} else {
if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 1 {
canDelete.Bool = true
canDelete.String = "correct"
} else {
canDelete.String = "invalid"
}
}
rl.viewPasteHandlerInner(w, r, flags, p, fu)
}
data := map[string]interface{}{
"Paste": p,
"FileExt": fileExt,
"CanDelete": canDelete,
}
rl.render(w, r, "pasteMeta", data)
return
func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPaste, p *db.Paste, fu *db.FileUpload) {
if flags&viewShowMeta != 0 {
rl.viewPasteHandlerInnerMeta(w, r, p, fu)
}
switch p.State {
@ -155,7 +158,7 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
switch p.Type {
case db.PasteTypeFileUpload:
if fu == nil {
panic(fmt.Sprintf("file for id %v does not exist in database\n", fuID))
panic(fmt.Sprintf("file for id %v does not exist in database\n", string(p.Content)))
}
location = fu.URL().String()
break
@ -176,6 +179,46 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
}
}
func (rl *rushlink) viewPasteHandlerInnerMeta(w http.ResponseWriter, r *http.Request, p *db.Paste, fu *db.FileUpload) {
var cd canDelete
deleteToken := getDeleteTokenFromRequest(r)
if deleteToken != "" {
if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 1 {
cd = canDeleteYes
} else {
cd = canDeleteNo
}
}
var fileExt string
if fu != nil {
fileExt = fu.Ext()
}
data := map[string]interface{}{
"Paste": p,
"FileExt": fileExt,
"CanDelete": cd,
"CanDeleteBool": cd.Bool(),
}
rl.render(w, r, "pasteMeta", data)
return
}
func (rl *rushlink) viewCreateSuccess(w http.ResponseWriter, r *http.Request, p *db.Paste, fu *db.FileUpload) {
var fileExt string
if fu != nil {
fileExt = fu.Ext()
}
data := map[string]interface{}{
"Paste": p,
"FileExt": fileExt,
"CanDelete": canDeleteYes,
"CanDeleteBool": true,
}
rl.render(w, r, "pasteMeta", data)
return
}
func (rl *rushlink) newPasteHandler(w http.ResponseWriter, r *http.Request) {
file, fileHeader, err := r.FormFile("file")
if err == nil {
@ -216,7 +259,7 @@ func (rl *rushlink) newFileUploadPasteHandler(w http.ResponseWriter, r *http.Req
}); err != nil {
panic(err)
}
rl.renderCreateSuccess(w, r, paste, fu)
rl.viewCreateSuccess(w, r, paste, fu)
}
func (rl *rushlink) newPasteHandlerURLEncoded(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
@ -256,7 +299,7 @@ func (rl *rushlink) newRedirectPasteHandler(w http.ResponseWriter, r *http.Reque
}); err != nil {
panic(err)
}
rl.renderCreateSuccess(w, r, paste, nil)
rl.viewCreateSuccess(w, r, paste, nil)
}
// Delete a URL from the database
@ -297,7 +340,7 @@ func (rl *rushlink) deletePasteHandler(w http.ResponseWriter, r *http.Request) {
rl.renderError(w, r, errorCode, fmt.Sprintf("error: %v\n", err))
return
}
rl.renderCreateSuccess(w, r, paste, nil)
rl.viewCreateSuccess(w, r, paste, nil)
}
// Add a new fileUpload redirect to the database

View File

@ -9,7 +9,6 @@ import (
"io"
"log"
"net/http"
"net/url"
"path/filepath"
"regexp"
"runtime/debug"
@ -19,8 +18,6 @@ import (
text "text/template"
"time"
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
"github.com/pkg/errors"
)
@ -168,22 +165,6 @@ func (rl *rushlink) renderInternalServerError(w http.ResponseWriter, r *http.Req
rl.renderError(w, r, http.StatusInternalServerError, msg)
}
func (rl *rushlink) renderCreateSuccess(w http.ResponseWriter, r *http.Request, paste *db.Paste, fu *db.FileUpload) {
if paste == nil {
panic("paste should not be nil")
}
var fileExt string
if fu != nil {
fileExt = fu.Ext()
}
var redirectURL url.URL
redirectURL.Path = fmt.Sprintf("/%s%s/meta", paste.Key, fileExt)
queryVals := redirectURL.Query()
queryVals.Add("deleteToken", paste.DeleteToken)
redirectURL.RawQuery = queryVals.Encode()
http.Redirect(w, r, redirectURL.String(), http.StatusFound)
}
// resolveHost constructs the `scheme://host` part of rushlinks public API.
//
// If the `--host` flag is set, it will return that URL.