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> <pre>
<a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}</a> <a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}</a>
--- ---
{{if and (ne .Paste.State.String "deleted") .CanDelete.Bool}} {{if and (ne .Paste.State.String "deleted") .CanDeleteBool}}
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> with delete token: <a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}</a>
{{else -}} {{else -}}
with delete token: &lt;unknown&gt; with delete token: &lt;unknown&gt;
{{end -}} {{end -}}
@ -18,18 +18,11 @@ created: unknown
{{else -}} {{else -}}
created: {{.Paste.TimeCreated}} created: {{.Paste.TimeCreated}}
{{end -}} {{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: curl --request "DELETE" "<a href="{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}">{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}"</a>
{{- /*
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>
``` ```
</pre> </pre>
{{end}} {{end}}

View File

@ -1,6 +1,6 @@
{{.Host}}/{{.Paste.Key}}{{.FileExt}} {{.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"}} with delete token: {{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Request.URL.Query.Get "deleteToken"}}
{{else -}} {{else -}}
with delete token: <unknown> with delete token: <unknown>
@ -11,17 +11,11 @@ created: unknown
{{else -}} {{else -}}
created: {{.Paste.TimeCreated}} created: {{.Paste.TimeCreated}}
{{end -}} {{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: # To delete this {{.Paste.Type}}, execute:
{{- /* curl --request "DELETE" "{{.Host}}/{{.Paste.Key}}{{.FileExt}}?deleteToken={{.Paste.DeleteToken}}"
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"}}"
``` ```
{{end}} {{end}}

View File

@ -28,6 +28,31 @@ const (
const cookieDeleteToken = "owner_token" 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) { func (rl *rushlink) staticGetHandler(w http.ResponseWriter, r *http.Request) {
rl.renderStatic(w, r, mux.Vars(r)["path"]) 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) { 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) { 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) { 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) vars := mux.Vars(r)
key := vars["key"] key := vars["key"]
var p *db.Paste var p *db.Paste
var fuID *uuid.UUID
var fu *db.FileUpload var fu *db.FileUpload
var fileExt string
if err := rl.db.Bolt.View(func(tx *bolt.Tx) error { if err := rl.db.Bolt.View(func(tx *bolt.Tx) error {
var err error var err error
p, err = db.GetPaste(tx, key) 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 { if p != nil && p.Type == db.PasteTypeFileUpload {
var id uuid.UUID var id uuid.UUID
copy(id[:], p.Content) copy(id[:], p.Content)
fuID = &id
fu, err = db.GetFileUpload(tx, id) fu, err = db.GetFileUpload(tx, id)
if err != nil { if err != nil {
return err return err
} }
fileExt = fu.Ext()
} }
return nil return nil
}); err != nil { }); err != nil {
@ -123,30 +144,12 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
return return
} }
if flags&viewShowMeta != 0 { rl.viewPasteHandlerInner(w, r, flags, p, fu)
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"
}
} }
data := map[string]interface{}{ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPaste, p *db.Paste, fu *db.FileUpload) {
"Paste": p, if flags&viewShowMeta != 0 {
"FileExt": fileExt, rl.viewPasteHandlerInnerMeta(w, r, p, fu)
"CanDelete": canDelete,
}
rl.render(w, r, "pasteMeta", data)
return
} }
switch p.State { switch p.State {
@ -155,7 +158,7 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
switch p.Type { switch p.Type {
case db.PasteTypeFileUpload: case db.PasteTypeFileUpload:
if fu == nil { 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() location = fu.URL().String()
break 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) { func (rl *rushlink) newPasteHandler(w http.ResponseWriter, r *http.Request) {
file, fileHeader, err := r.FormFile("file") file, fileHeader, err := r.FormFile("file")
if err == nil { if err == nil {
@ -216,7 +259,7 @@ func (rl *rushlink) newFileUploadPasteHandler(w http.ResponseWriter, r *http.Req
}); err != nil { }); err != nil {
panic(err) 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) { 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 { }); err != nil {
panic(err) panic(err)
} }
rl.renderCreateSuccess(w, r, paste, nil) rl.viewCreateSuccess(w, r, paste, nil)
} }
// Delete a URL from the database // 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)) rl.renderError(w, r, errorCode, fmt.Sprintf("error: %v\n", err))
return return
} }
rl.renderCreateSuccess(w, r, paste, nil) rl.viewCreateSuccess(w, r, paste, nil)
} }
// Add a new fileUpload redirect to the database // Add a new fileUpload redirect to the database

View File

@ -9,7 +9,6 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"net/url"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime/debug" "runtime/debug"
@ -19,8 +18,6 @@ import (
text "text/template" text "text/template"
"time" "time"
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
"github.com/pkg/errors" "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) 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. // resolveHost constructs the `scheme://host` part of rushlinks public API.
// //
// If the `--host` flag is set, it will return that URL. // If the `--host` flag is set, it will return that URL.