flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 9cf9bb53cbc58129e195c2ae937abce4216b3566
parent 4e70232a94d9c007adea317afd85d2f115160912
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sun,  6 Dec 2020 00:54:24 -0800

some refactoring WIP

Diffstat:
Mhttp.go | 153+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtemplates/me.html | 4++--
Mtemplates/my_site.html | 4++--
Mtemplates/nav.html | 4++--
Mtemplates/reset_pass.html | 39++++++++++++++++++++++++++-------------
Mutils.go | 11+++++++++++
6 files changed, 118 insertions(+), 97 deletions(-)

diff --git a/http.go b/http.go @@ -56,32 +56,25 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { return } - authd, _, isAdmin := getAuthUser(r) - indexFiles, err := getIndexFiles(isAdmin) + user := newGetAuthUser(r) + indexFiles, err := getIndexFiles(user.IsAdmin) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } allUsers, err := getActiveUserNames() if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } data := struct { Host string PageTitle string Files []*File Users []string - LoggedIn bool - IsAdmin bool - }{c.Host, c.SiteTitle, indexFiles, allUsers, authd, isAdmin} + AuthUser AuthUser + }{c.Host, c.SiteTitle, indexFiles, allUsers, user} err = t.ExecuteTemplate(w, "index.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } @@ -113,9 +106,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { fileBytes, err = ioutil.ReadAll(f) } if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } data := struct { FileName string @@ -127,9 +118,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { }{fileName, string(fileBytes), c.SiteTitle, authUser, c.Host, isText} err = t.ExecuteTemplate(w, "edit_file.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } else if r.Method == "POST" { // get post body @@ -156,9 +145,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { return } if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } newName := filepath.Clean(r.Form.Get("rename")) err = checkIfValidFile(newName, fileBytes) @@ -179,9 +166,8 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { - session, _ := SessionStore.Get(r, "cookie-session") - authUser, ok := session.Values["auth_user"].(string) - if !ok { + user := newGetAuthUser(r) + if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return } @@ -201,16 +187,14 @@ func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { renderError(w, err.Error(), http.StatusBadRequest) return } - destPath := path.Join(c.FilesDirectory, authUser, fileName) + destPath := path.Join(c.FilesDirectory, user.Username, fileName) f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } defer f.Close() - if userHasSpace(authUser, c.MaxFileBytes) { // Not quite right + if userHasSpace(user.Username, c.MaxFileBytes) { // Not quite right io.Copy(f, bytes.NewReader(dest)) } else { renderError(w, fmt.Sprintf("Bad Request: Out of file space. Max space: %d.", c.MaxUserBytes), http.StatusBadRequest) @@ -220,6 +204,28 @@ func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/my_site", http.StatusSeeOther) } +// TODO use this +type AuthUser struct { + LoggedIn bool + Username string + IsAdmin bool + ImpersonatingUser string // used if impersonating +} + +func newGetAuthUser(r *http.Request) AuthUser { + session, _ := SessionStore.Get(r, "cookie-session") + user, ok := session.Values["auth_user"].(string) + impers, _ := session.Values["impersonating_user"].(string) + isAdmin, _ := session.Values["admin"].(bool) + return AuthUser{ + LoggedIn: ok, + Username: user, + IsAdmin: isAdmin, + ImpersonatingUser: impers, + } +} + +//TODO deprecate func getAuthUser(r *http.Request) (bool, string, bool) { session, _ := SessionStore.Get(r, "cookie-session") user, ok := session.Values["auth_user"].(string) @@ -227,62 +233,56 @@ func getAuthUser(r *http.Request) (bool, string, bool) { return ok, user, isAdmin } func deleteFileHandler(w http.ResponseWriter, r *http.Request) { - authd, authUser, _ := getAuthUser(r) - if !authd { + user := newGetAuthUser(r) + if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return } - fileName := filepath.Clean(r.URL.Path[len("/delete/"):]) - filePath := path.Join(c.FilesDirectory, authUser, fileName) + filePath := safeGetFilePath(user.Username, r.URL.Path[len("/delete/"):]) if r.Method == "POST" { - os.Remove(filePath) // suppress error + os.Remove(filePath) // TODO handle error } http.Redirect(w, r, "/my_site", http.StatusSeeOther) } func mySiteHandler(w http.ResponseWriter, r *http.Request) { - authd, authUser, isAdmin := getAuthUser(r) - if !authd { + user := newGetAuthUser(r) + if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return } // check auth - userFolder := path.Join(c.FilesDirectory, authUser) - files, _ := getMyFilesRecursive(userFolder, authUser) + userFolder := getUserDirectory(user.Username) + files, _ := getMyFilesRecursive(userFolder, user.Username) data := struct { Host string PageTitle string - AuthUser string Files []*File - LoggedIn bool - IsAdmin bool - }{c.Host, c.SiteTitle, authUser, files, authd, isAdmin} + AuthUser AuthUser + }{c.Host, c.SiteTitle, files, user} _ = t.ExecuteTemplate(w, "my_site.html", data) } func myAccountHandler(w http.ResponseWriter, r *http.Request) { - authd, authUser, isAdmin := getAuthUser(r) - if !authd { + user := newGetAuthUser(r) + authUser := user.Username + if !user.LoggedIn { renderDefaultError(w, http.StatusForbidden) return } - me, _ := getUserByName(authUser) + me, _ := getUserByName(user.Username) type pageData struct { PageTitle string - LoggedIn bool - AuthUser string - IsAdmin bool + AuthUser AuthUser Email string Errors []string } - data := pageData{"My Account", true, authUser, isAdmin, me.Email, nil} + data := pageData{"My Account", user, me.Email, nil} if r.Method == "GET" { err := t.ExecuteTemplate(w, "me.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } else if r.Method == "POST" { r.ParseForm() @@ -312,9 +312,9 @@ func myAccountHandler(w http.ResponseWriter, r *http.Request) { } } // reset auth - authd, authUser, isAdmin = getAuthUser(r) + user = newGetAuthUser(r) data.Errors = errors - data.AuthUser = authUser + data.AuthUser = user data.Email = newEmail _ = t.ExecuteTemplate(w, "me.html", data) } @@ -330,9 +330,7 @@ func archiveHandler(w http.ResponseWriter, r *http.Request) { userFolder := filepath.Join(c.FilesDirectory, filepath.Clean(authUser)) err := zipit(userFolder, w) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } @@ -346,9 +344,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { }{"", "Login"} err := t.ExecuteTemplate(w, "login.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } else if r.Method == "POST" { r.ParseForm() @@ -382,9 +378,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { }{"Invalid login or password", c.SiteTitle} err := t.ExecuteTemplate(w, "login.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } } @@ -422,9 +416,7 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { }{c.Host, nil, "Register"} err := t.ExecuteTemplate(w, "register.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } else if r.Method == "POST" { r.ParseForm() @@ -489,9 +481,7 @@ func adminHandler(w http.ResponseWriter, r *http.Request) { }{allUsers, true, true, "Admin", c.Host} err = t.ExecuteTemplate(w, "admin.html", data) if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + panic(err) } } @@ -558,13 +548,19 @@ func userFile(w http.ResponseWriter, r *http.Request) { func deleteAccountHandler(w http.ResponseWriter, r *http.Request) { _, authUser, _ := getAuthUser(r) - err := deleteUser(authUser) - if err != nil { - log.Println(err) - renderDefaultError(w, http.StatusInternalServerError) - return + if r.Method == "POST" { + err := deleteUser(authUser) + if err != nil { + log.Println(err) + renderDefaultError(w, http.StatusInternalServerError) + return + } + logoutHandler(w, r) } - logoutHandler(w, r) +} + +func resetPasswordHandler(w http.ResponseWriter, r *http.Request) { + getAuthUser(r) } func adminUserHandler(w http.ResponseWriter, r *http.Request) { @@ -621,13 +617,14 @@ func runHTTPServer() { serveMux.HandleFunc(hostname+"/register", registerHandler) serveMux.HandleFunc(hostname+"/delete/", deleteFileHandler) serveMux.HandleFunc(hostname+"/delete-account", deleteAccountHandler) + serveMux.HandleFunc(hostname+"/reset-password", resetPasswordHandler) // admin commands serveMux.HandleFunc(hostname+"/admin/user/", adminUserHandler) // TODO rate limit login https://github.com/ulule/limiter - wrapped := handlers.LoggingHandler(log.Writer(), serveMux) + wrapped := (handlers.LoggingHandler(log.Writer(), handlers.RecoveryHandler()(serveMux))) // handle user files based on subdomain serveMux.HandleFunc("/", userFile) diff --git a/templates/me.html b/templates/me.html @@ -9,7 +9,7 @@ name="username" size="32" type="text" - value="{{.AuthUser}}" + value="{{.AuthUser.Username}}" /> </div> <div> @@ -27,7 +27,7 @@ /> </div> </form> -<a href="/reset_password">Reset password</a> +<a href="/reset-password">Reset password</a> <p><a href="/my_site/flounder-archive.zip">🗄️ Download my site archive (.zip)</a></p> <form action="/delete-account" method="POST" class="inline"> <input diff --git a/templates/my_site.html b/templates/my_site.html @@ -1,9 +1,9 @@ {{$domain := .Host}} -{{$authUser := .AuthUser}} +{{$authUser := .AuthUser.Username}} {{template "header" .}} <h1>Managing <a href="//{{$authUser}}.{{$domain}}"> - {{.AuthUser}}.{{$domain}} + {{$authUser}}.{{$domain}} </a> </h1> {{template "nav.html" .}} diff --git a/templates/nav.html b/templates/nav.html @@ -1,9 +1,9 @@ <nav> <a href="/">home</a> -{{ if .LoggedIn }} +{{ if .AuthUser.LoggedIn }} <a href="/my_site">my_site</a> <a href="/me">me</a> - {{ if .IsAdmin }} + {{ if .AuthUser.IsAdmin }} <a href="/admin">admin</a> {{ end }} <a href="/logout">logout</a> diff --git a/templates/reset_pass.html b/templates/reset_pass.html @@ -2,29 +2,42 @@ <h1>Reset Password</h1> <form action="/reset_password" method="post"> <div> - <label for="username">Username</label><br> + <label for="password">Current Password</label><br> <input - id="username" - name="username" + id="password" + name="password" size="32" type="text" value="{{.AuthUser}}" /> </div> <div> - <label for="email">Email</label> - <input id="email" name="email" size="64" type="text" value="{{.Email}}" /> + <label for="new_password1">New Password</label><br> + <input + id="new_password1" + name="new_password1" + size="32" + type="text" + value="" + /> </div> - <div class="error">{{ range .Errors}}{{.}}<br>{{end}} </div> <div> + <label for="new_password2">New Password (repeat)</label><br> <input - class="button" - id="submit" - name="submit" - type="submit" - value="Save" + id="new_password2" + name="new_password2" + size="32" + type="text" + value="" /> - </div> + </div> + <input + class="button" + id="submit" + name="submit" + type="submit" + value="Change" +/> + </form> -<a href="/reset_password">Reset password</a> {{template "footer" .}} diff --git a/utils.go b/utils.go @@ -40,6 +40,17 @@ func timeago(t *time.Time) string { } } +// safe +func getUserDirectory(username string) string { + // extra filepath.clean just to be safe + userFolder := path.Join(c.FilesDirectory, filepath.Clean(username)) + return userFolder +} + +func safeGetFilePath(username string, filename string) string { + return path.Join(getUserDirectory(username), filepath.Clean(filename)) +} + // TODO move into checkIfValidFile. rename it func userHasSpace(user string, newBytes int) bool { userPath := path.Join(c.FilesDirectory, user)