1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +00:00

Sign edits and refactor mutators a little

This commit is contained in:
bouncepaw 2020-11-18 18:07:53 +05:00
parent 57751d03f4
commit b30c368c48
5 changed files with 93 additions and 73 deletions

View File

@ -8,6 +8,7 @@ import (
"path/filepath"
"sync"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -29,7 +30,7 @@ const (
type HistoryOp struct {
// All errors are appended here.
Errs []error
opType OpType
Type OpType
userMsg string
name string
email string
@ -40,7 +41,9 @@ func Operation(opType OpType) *HistoryOp {
gitMutex.Lock()
hop := &HistoryOp{
Errs: []error{},
opType: opType,
name: "anon",
email: "anon@mycorrhiza",
Type: opType,
}
return hop
}
@ -116,9 +119,11 @@ func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
return hop
}
// WithSignature sets a signature for the future commit. You need to pass a username only, the rest is upon us (including email and time).
func (hop *HistoryOp) WithSignature(username string) *HistoryOp {
hop.name = username
hop.email = username + "@mycorrhiza" // A fake email, why not
// WithUser sets a user for the commit.
func (hop *HistoryOp) WithUser(u *user.User) *HistoryOp {
if u.Group != user.UserAnon {
hop.name = u.Name
hop.email = u.Name + "@mycorrhiza"
}
return hop
}

View File

@ -11,12 +11,14 @@ import (
)
func init() {
http.HandleFunc("/upload-binary/", handlerUploadBinary)
http.HandleFunc("/upload-text/", handlerUploadText)
// Those that do not actually mutate anything:
http.HandleFunc("/edit/", handlerEdit)
http.HandleFunc("/delete-ask/", handlerDeleteAsk)
http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
http.HandleFunc("/rename-ask/", handlerRenameAsk)
// And those that do mutate something:
http.HandleFunc("/upload-binary/", handlerUploadBinary)
http.HandleFunc("/upload-text/", handlerUploadText)
http.HandleFunc("/delete-confirm/", handlerDeleteConfirm)
http.HandleFunc("/rename-confirm/", handlerRenameConfirm)
}
@ -38,20 +40,16 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
var (
hyphaName = HyphaNameFromRq(rq, "rename-confirm")
hyphaData, isOld = HyphaStorage[hyphaName]
_, isOld = HyphaStorage[hyphaName]
newName = CanonicalName(rq.PostFormValue("new-name"))
_, newNameIsUsed = HyphaStorage[newName]
recursive bool
recursive = rq.PostFormValue("recursive") == "true"
u = user.FromRequest(rq).OrAnon()
)
if ok := user.CanProceed(rq, "rename-confirm"); !ok {
switch {
case !u.CanProceed("rename-confirm"):
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a trusted editor to rename pages.")
log.Println("Rejected", rq.URL)
return
}
if rq.PostFormValue("recursive") == "true" {
recursive = true
}
switch {
case newNameIsUsed:
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: hypha exists",
fmt.Sprintf("Hypha named <a href='/page/%s'>%s</a> already exists.", hyphaName, hyphaName))
@ -65,12 +63,12 @@ func handlerRenameConfirm(w http.ResponseWriter, rq *http.Request) {
HttpErr(w, http.StatusBadRequest, hyphaName, "Error: invalid name",
"Invalid new name. Names cannot contain characters <code>^?!:#@&gt;&lt;*|\"\\'&amp;%</code>")
default:
if hop := hyphaData.RenameHypha(hyphaName, newName, recursive); len(hop.Errs) == 0 {
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
} else {
if hop := RenameHypha(hyphaName, newName, recursive, u); len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName,
"Error: could not rename hypha",
fmt.Sprintf("Could not rename this hypha due to an internal error. Server errors: <code>%v</code>", hop.Errs))
} else {
http.Redirect(w, rq, "/page/"+newName, http.StatusSeeOther)
}
}
}
@ -96,27 +94,27 @@ func handlerDeleteConfirm(w http.ResponseWriter, rq *http.Request) {
var (
hyphaName = HyphaNameFromRq(rq, "delete-confirm")
hyphaData, isOld = HyphaStorage[hyphaName]
u = user.FromRequest(rq)
)
if ok := user.CanProceed(rq, "delete-confirm"); !ok {
if !u.CanProceed("delete-confirm") {
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be a moderator to delete pages.")
log.Println("Rejected", rq.URL)
return
}
if isOld {
// If deleted successfully
if hop := hyphaData.DeleteHypha(hyphaName); len(hop.Errs) == 0 {
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
} else {
HttpErr(w, http.StatusInternalServerError, hyphaName,
"Error: could not delete hypha",
fmt.Sprintf("Could not delete this hypha due to an internal error. Server errors: <code>%v</code>", hop.Errs))
}
} else {
if !isOld {
// The precondition is to have the hypha in the first place.
HttpErr(w, http.StatusPreconditionFailed, hyphaName,
"Error: no such hypha",
"Could not delete this hypha because it does not exist.")
return
}
if hop := hyphaData.DeleteHypha(hyphaName, u); len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName,
"Error: could not delete hypha",
fmt.Sprintf("Could not delete this hypha due to internal errors. Server errors: <code>%v</code>", hop.Errs))
return
}
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}
// handlerEdit shows the edit form. It doesn't edit anything actually.
@ -152,22 +150,19 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
var (
hyphaName = HyphaNameFromRq(rq, "upload-text")
hyphaData, isOld = HyphaStorage[hyphaName]
textData = rq.PostFormValue("text")
u = user.FromRequest(rq).OrAnon()
)
if ok := user.CanProceed(rq, "upload-text"); !ok {
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to edit pages.")
log.Println("Rejected", rq.URL)
return
}
if !isOld {
hyphaData = &HyphaData{}
}
if textData == "" {
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No text data passed")
return
}
if hop := hyphaData.UploadText(hyphaName, textData, isOld); len(hop.Errs) != 0 {
if hop := UploadText(hyphaName, textData, u); len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
} else {
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
@ -177,36 +172,37 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
// handlerUploadBinary uploads a new binary part for the hypha.
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
log.Println(rq.URL)
hyphaName := HyphaNameFromRq(rq, "upload-binary")
if ok := user.CanProceed(rq, "upload-binary"); !ok {
var (
hyphaName = HyphaNameFromRq(rq, "upload-binary")
u = user.FromRequest(rq)
)
if !u.CanProceed("upload-binary") {
HttpErr(w, http.StatusForbidden, hyphaName, "Not enough rights", "You must be an editor to upload attachments.")
log.Println("Rejected", rq.URL)
return
}
rq.ParseMultipartForm(10 << 20)
rq.ParseMultipartForm(10 << 20) // Set upload limit
file, handler, err := rq.FormFile("binary")
if file != nil {
defer file.Close()
}
// If file is not passed:
if err != nil {
HttpErr(w, http.StatusBadRequest, hyphaName, "Error", "No binary data passed")
return
}
// If file is passed:
var (
hyphaData, isOld = HyphaStorage[hyphaName]
mime = handler.Header.Get("Content-Type")
hop = UploadBinary(hyphaName, mime, file, u)
)
if !isOld {
hyphaData = &HyphaData{}
}
hop := hyphaData.UploadBinary(hyphaName, mime, file, isOld)
if len(hop.Errs) != 0 {
HttpErr(w, http.StatusInternalServerError, hyphaName, "Error", hop.Errs[0].Error())
} else {
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
return
}
http.Redirect(w, rq, "/page/"+hyphaName, http.StatusSeeOther)
}

View File

@ -12,6 +12,7 @@ import (
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/markup"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -33,6 +34,15 @@ func init() {
}
}
// GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist.
func GetHyphaData(hyphaName string) (hyphaData *HyphaData, isOld bool) {
hyphaData, isOld = HyphaStorage[hyphaName]
if hyphaData == nil {
hyphaData = &HyphaData{}
}
return
}
// HyphaData represents a hypha's meta information: binary and text parts rooted paths and content types.
type HyphaData struct {
textPath string
@ -40,10 +50,16 @@ type HyphaData struct {
}
// uploadHelp is a helper function for UploadText and UploadBinary
func (hd *HyphaData) uploadHelp(hop *history.HistoryOp, hyphaName, ext string, originalFullPath *string, isOld bool, data []byte) *history.HistoryOp {
func uploadHelp(hop *history.HistoryOp, hyphaName, ext string, data []byte, u *user.User) *history.HistoryOp {
var (
hyphaData, isOld = GetHyphaData(hyphaName)
fullPath = filepath.Join(WikiDir, hyphaName+ext)
originalFullPath = &hyphaData.textPath
)
if hop.Type == history.TypeEditBinary {
originalFullPath = &hyphaData.binaryPath
}
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
return hop.WithError(err)
}
@ -51,30 +67,34 @@ func (hd *HyphaData) uploadHelp(hop *history.HistoryOp, hyphaName, ext string, o
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
return hop.WithError(err)
}
if isOld && *originalFullPath != fullPath && *originalFullPath != "" {
if err := history.Rename(*originalFullPath, fullPath); err != nil {
return hop.WithError(err)
}
log.Println("Move", *originalFullPath, "to", fullPath)
}
// New hyphae must be added to the hypha storage
if !isOld {
HyphaStorage[hyphaName] = hd
HyphaStorage[hyphaName] = hyphaData
}
*originalFullPath = fullPath
log.Printf("%v\n", *hd)
return hop.WithFiles(fullPath).
WithSignature("anon").
WithUser(u).
Apply()
}
// UploadText loads a new text part from `textData` for hypha `hyphaName` with `hd`. It must be marked if the hypha `isOld`.
func (hd *HyphaData) UploadText(hyphaName, textData string, isOld bool) *history.HistoryOp {
hop := history.Operation(history.TypeEditText).WithMsg(fmt.Sprintf("Edit %s", hyphaName))
return hd.uploadHelp(hop, hyphaName, ".myco", &hd.textPath, isOld, []byte(textData))
// UploadText loads a new text part from `textData` for hypha `hyphaName`.
func UploadText(hyphaName, textData string, u *user.User) *history.HistoryOp {
return uploadHelp(
history.
Operation(history.TypeEditText).
WithMsg(fmt.Sprintf("Edit %s", hyphaName)),
hyphaName, ".myco", []byte(textData), u)
}
// UploadBinary loads a new binary part from `file` for hypha `hyphaName` with `hd`. The contents have the specified `mime` type. It must be marked if the hypha `isOld`.
func (hd *HyphaData) UploadBinary(hyphaName, mime string, file multipart.File, isOld bool) *history.HistoryOp {
func UploadBinary(hyphaName, mime string, file multipart.File, u *user.User) *history.HistoryOp {
var (
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for %s with type %s", hyphaName, mime))
data, err = ioutil.ReadAll(file)
@ -82,16 +102,15 @@ func (hd *HyphaData) UploadBinary(hyphaName, mime string, file multipart.File, i
if err != nil {
return hop.WithError(err).Apply()
}
return hd.uploadHelp(hop, hyphaName, MimeToExtension(mime), &hd.binaryPath, isOld, data)
return uploadHelp(hop, hyphaName, MimeToExtension(mime), data, u)
}
// DeleteHypha deletes hypha and makes a history record about that.
func (hd *HyphaData) DeleteHypha(hyphaName string) *history.HistoryOp {
func (hd *HyphaData) DeleteHypha(hyphaName string, u *user.User) *history.HistoryOp {
hop := history.Operation(history.TypeDeleteHypha).
WithFilesRemoved(hd.textPath, hd.binaryPath).
WithMsg(fmt.Sprintf("Delete %s", hyphaName)).
WithSignature("anon").
WithUser(u).
Apply()
if len(hop.Errs) == 0 {
delete(HyphaStorage, hyphaName)
@ -138,7 +157,7 @@ func relocateHyphaData(hyphaNames []string, replaceName func(string) string) {
}
// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way.
func (hd *HyphaData) RenameHypha(hyphaName, newName string, recursive bool) *history.HistoryOp {
func RenameHypha(hyphaName, newName string, recursive bool, u *user.User) *history.HistoryOp {
var (
replaceName = func(str string) string {
return strings.Replace(str, hyphaName, newName, 1)
@ -157,7 +176,7 @@ func (hd *HyphaData) RenameHypha(hyphaName, newName string, recursive bool) *his
}
hop.WithFilesRenamed(renameMap).
WithMsg(fmt.Sprintf(renameMsg, hyphaName, newName)).
WithSignature("anon").
WithUser(u).
Apply()
if len(hop.Errs) == 0 {
relocateHyphaData(hyphaNames, replaceName)

View File

@ -62,9 +62,9 @@ func (ug UserGroup) CanAccessRoute(route string) bool {
}
func CanProceed(rq *http.Request, route string) bool {
ug := UserAnon
if u := FromRequest(rq); u != nil {
ug = u.Group
}
return ug.CanAccessRoute(route)
return FromRequest(rq).OrAnon().CanProceed(route)
}
func (u *User) CanProceed(route string) bool {
return u.Group.CanAccessRoute(route)
}

View File

@ -44,7 +44,7 @@ func FromRequest(rq *http.Request) *User {
if err != nil {
return nil
}
return UserStorage.userByToken(cookie.Value)
return UserStorage.userByToken(cookie.Value).OrAnon()
}
func LoginDataHTTP(w http.ResponseWriter, rq *http.Request, username, password string) string {