1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-06 10:00:26 +00:00

Make the lock work

There is a new config field: Authorization.Locked.

I am a little bit sorry for how actually the lock is implemented. I've added the check on almost every handler there is. Good luck maintaining that ❤️
This commit is contained in:
Timur Ismagilov 2021-07-11 00:04:21 +05:00
parent d8f4f40f52
commit 5d45ae67d4
9 changed files with 123 additions and 26 deletions

View File

@ -23,13 +23,13 @@ var (
UserHypha string
HeaderLinksHypha string
ListenAddr string
URL string
GeminiCertificatePath string
ListenAddr string
URL string
UseAuth bool
AllowRegistration bool
RegistrationLimit uint64
Locked bool
CommonScripts []string
ViewScripts []string
@ -82,6 +82,7 @@ type Authorization struct {
UseAuth bool
AllowRegistration bool
RegistrationLimit uint64 `comment:"This field controls the maximum amount of allowed registrations."`
Locked bool `comment:"Set if users have to authorize to see anything on the wiki."`
}
// ReadConfigFile reads a config on the given path and stores the
@ -103,6 +104,7 @@ func ReadConfigFile(path string) error {
UseAuth: false,
AllowRegistration: false,
RegistrationLimit: 0,
Locked: false,
},
CustomScripts: CustomScripts{
CommonScripts: []string{},
@ -135,7 +137,9 @@ func ReadConfigFile(path string) error {
// Map the config file to the config struct. It'll do nothing if the file
// doesn't exist or is empty.
f.MapTo(cfg)
if err := f.MapTo(cfg); err != nil {
return err
}
// Map the struct to the global variables
WikiName = cfg.WikiName
@ -150,6 +154,7 @@ func ReadConfigFile(path string) error {
UseAuth = cfg.UseAuth
AllowRegistration = cfg.AllowRegistration
RegistrationLimit = cfg.RegistrationLimit
Locked = cfg.Locked && cfg.UseAuth // Makes no sense to have the lock but no auth
CommonScripts = cfg.CommonScripts
ViewScripts = cfg.ViewScripts
EditScripts = cfg.EditScripts

View File

@ -1,6 +1,7 @@
package user
import (
"net/http"
"sync"
"time"
@ -96,3 +97,12 @@ func (user *User) isCorrectPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
return err == nil
}
// ShowLockMaybe redirects to the lock page if the user is anon and the wiki has been configured to use the lock. It returns true if the user was redirected.
func (user *User) ShowLockMaybe(w http.ResponseWriter, rq *http.Request) bool {
if cfg.Locked && user.Group == "anon" {
http.Redirect(w, rq, "/lock", http.StatusSeeOther)
return true
}
return false
}

View File

@ -30,6 +30,9 @@ func initAdmin() {
// handlerAdmin provides the admin panel.
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if user.CanProceed(rq, "admin") {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
@ -43,6 +46,9 @@ func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
// handlerAdminShutdown kills the wiki.
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
log.Fatal("An admin commanded the wiki to shutdown")
}
@ -51,6 +57,9 @@ func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
// handlerAdminReindexUsers reinitialises the user system.
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
user.ReadUsersFromFilesystem()
redirectTo := rq.Referer()
@ -61,10 +70,13 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
}
}
func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
util.PrepareRq(r)
if user.CanProceed(r, "admin") {
path := strings.TrimPrefix(r.URL.Path, "/admin/users")
func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if user.CanProceed(rq, "admin") {
path := strings.TrimPrefix(rq.URL.Path, "/admin/users")
parts := strings.Split(path, "/")[1:]
// Users dashboard
@ -81,7 +93,7 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
})
html := views.AdminUsersPanelHTML(userList)
html = views.BaseHTML("Manage users", html, user.FromRequest(r))
html = views.BaseHTML("Manage users", html, user.FromRequest(rq))
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
if _, err := io.WriteString(w, html); err != nil {
@ -103,9 +115,9 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
switch parts[1] {
case "edit":
f := util.FormDataFromRequest(r, []string{"group"})
f := util.FormDataFromRequest(rq, []string{"group"})
if r.Method == http.MethodPost {
if rq.Method == http.MethodPost {
oldGroup := u.Group
newGroup := f.Get("group")
@ -116,7 +128,7 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
log.Println(err)
f = f.WithError(err)
} else {
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
return
}
} else {
@ -127,7 +139,7 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
f.Put("group", u.Group)
html := views.AdminUserEditHTML(u, f)
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(rq))
if f.HasError() {
w.WriteHeader(http.StatusBadRequest)
@ -138,17 +150,17 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
case "delete":
f := util.NewFormData()
if r.Method == http.MethodPost {
if rq.Method == http.MethodPost {
f = f.WithError(user.DeleteUser(u.Name))
if !f.HasError() {
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
} else {
log.Println(f.Error())
}
}
html := views.AdminUserDeleteHTML(u, util.NewFormData())
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(rq))
if f.HasError() {
w.WriteHeader(http.StatusBadRequest)
@ -162,32 +174,35 @@ func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
}
}
func handlerAdminUserNew(w http.ResponseWriter, r *http.Request) {
util.PrepareRq(r)
if user.CanProceed(r, "admin") {
if r.Method == http.MethodGet {
func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if user.CanProceed(rq, "admin") {
if rq.Method == http.MethodGet {
// New user form
html := views.AdminUserNewHTML(util.NewFormData())
html = views.BaseHTML("New user", html, user.FromRequest(r))
html = views.BaseHTML("New user", html, user.FromRequest(rq))
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
io.WriteString(w, html)
return
} else if r.Method == http.MethodPost {
} else if rq.Method == http.MethodPost {
// Create a user
f := util.FormDataFromRequest(r, []string{"name", "password", "group"})
f := util.FormDataFromRequest(rq, []string{"name", "password", "group"})
err := user.Register(f.Get("name"), f.Get("password"), f.Get("group"), true)
if err != nil {
html := views.AdminUserNewHTML(f.WithError(err))
html = views.BaseHTML("New user", html, user.FromRequest(r))
html = views.BaseHTML("New user", html, user.FromRequest(rq))
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
io.WriteString(w, html)
} else {
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
}
return
}

View File

@ -33,6 +33,9 @@ func handlerLock(w http.ResponseWriter, rq *http.Request) {
// handlerRegister both displays the register form (GET) and registers users (POST).
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
util.PrepareRq(rq)
if !cfg.AllowRegistration {
w.WriteHeader(http.StatusForbidden)
@ -102,6 +105,9 @@ func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
// handlerLogin shows the login form.
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
util.PrepareRq(rq)
w.Header().Set("Content-Type", "text/html;charset=utf-8")
if cfg.UseAuth {

View File

@ -24,6 +24,9 @@ func initHistory() {
// handlerHistory lists all revisions of a hypha.
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
hyphaName := util.HyphaNameFromRq(rq, "history")
var list string
@ -41,6 +44,9 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
// handlerRecentChanges displays the /recent-changes/ page.
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
var (
noPrefix = strings.TrimPrefix(rq.URL.String(), "/recent-changes/")
n, err = strconv.Atoi(noPrefix)
@ -55,6 +61,9 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
// genericHandlerOfFeeds is a helper function for the web feed handlers.
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func() (string, error), name string) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if content, err := f(); err != nil {
w.Header().Set("Content-Type", "text/plain;charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)

View File

@ -42,6 +42,9 @@ func factoryHandlerAsker(
h = hyphae.ByName(hyphaName)
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if err, errtitle := asker(u, h); err != nil {
httpErr(
w,
@ -92,6 +95,9 @@ func factoryHandlerConfirmer(
h = hyphae.ByName(hyphaName)
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if hop, errtitle := confirmer(h, u, rq); hop.HasErrors() {
httpErr(w, http.StatusInternalServerError, hyphaName,
errtitle,
@ -139,6 +145,9 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
err error
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if err, errtitle := shroom.CanEdit(u, h); err != nil {
httpErr(w, http.StatusInternalServerError, hyphaName,
errtitle,
@ -178,6 +187,9 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
hop *history.HistoryOp
errtitle string
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if action != "Preview" {
hop, errtitle = shroom.UploadText(h, []byte(textData), message, u)
@ -219,6 +231,9 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
u = user.FromRequest(rq)
file, handler, err = rq.FormFile("binary")
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if err != nil {
httpErr(w, http.StatusInternalServerError, hyphaName,
"Error",

View File

@ -37,6 +37,9 @@ func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
h = hyphae.ByName(hyphaName)
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
util.HTTP200Page(w,
views.BaseHTML(
fmt.Sprintf("Attachment of %s", util.BeautifulName(hyphaName)),
@ -54,6 +57,9 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
h = hyphae.ByName(hyphaName)
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
util.HTTP200Page(w,
views.BaseHTML(
fmt.Sprintf("Diff of %s at %s", hyphaName, revHash),
@ -74,6 +80,9 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
textContents, err = history.FileAtRevision(h.TextPath, revHash)
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if err == nil {
ctx, _ := mycocontext.ContextFromStringInput(hyphaName, textContents)
contents = mycomarkup.BlocksToHTML(ctx, mycomarkup.BlockTree(ctx))
@ -92,6 +101,9 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
// handlerText serves raw source text of the hypha.
func handlerText(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
hyphaName := util.HyphaNameFromRq(rq, "text")
if h := hyphae.ByName(hyphaName); h.Exists {
log.Println("Serving", h.TextPath)
@ -103,6 +115,9 @@ func handlerText(w http.ResponseWriter, rq *http.Request) {
// handlerBinary serves binary part of the hypha.
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
hyphaName := util.HyphaNameFromRq(rq, "binary")
if h := hyphae.ByName(hyphaName); h.Exists {
log.Println("Serving", h.BinaryPath)
@ -121,6 +136,9 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
openGraph string
u = user.FromRequest(rq)
)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
if h.Exists {
fileContentsT, errT := os.ReadFile(h.TextPath)
_, errB := os.Stat(h.BinaryPath)

View File

@ -30,13 +30,20 @@ func initStuff() {
// handlerList shows a list of all hyphae in the wiki in random order.
func handlerList(w http.ResponseWriter, rq *http.Request) {
u := user.FromRequest(rq)
if shown := u.ShowLockMaybe(w, rq); shown {
return
}
util.PrepareRq(rq)
util.HTTP200Page(w, views.BaseHTML("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
util.HTTP200Page(w, views.BaseHTML("List of pages", views.HyphaListHTML(), u))
}
// handlerReindex reindexes all hyphae by checking the wiki storage directory anew.
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if ok := user.CanProceed(rq, "reindex"); !ok {
httpErr(w, http.StatusForbidden, cfg.HomeHypha, "Not enough rights", "You must be an admin to reindex hyphae.")
log.Println("Rejected", rq.URL)
@ -54,6 +61,9 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
// See https://mycorrhiza.wiki/hypha/configuration/header
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
if ok := user.CanProceed(rq, "update-header-links"); !ok {
httpErr(w, http.StatusForbidden, cfg.HomeHypha, "Not enough rights", "You must be a moderator to update header links.")
log.Println("Rejected", rq.URL)
@ -66,6 +76,9 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
// handlerRandom redirects to a random hypha.
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
var (
randomHyphaName string
amountOfHyphae = hyphae.Count()
@ -87,6 +100,9 @@ func handlerRandom(w http.ResponseWriter, rq *http.Request) {
// handlerAbout shows a summary of wiki's software.
func handlerAbout(w http.ResponseWriter, rq *http.Request) {
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err := io.WriteString(w, views.BaseHTML("About "+cfg.WikiName, views.AboutHTML(), user.FromRequest(rq)))

View File

@ -54,6 +54,9 @@ func handlerStyle(w http.ResponseWriter, rq *http.Request) {
}
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
if shown := user.FromRequest(rq).ShowLockMaybe(w, rq); shown {
return
}
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
w.WriteHeader(http.StatusOK)
w.Write([]byte(views.BaseHTML("User list", views.UserListHTML(), user.FromRequest(rq))))