fierj
PublicTiny personal git forge
package main
import (
"fmt"
"html/template"
"net/http"
"strings"
)
// ----- Login -----
func LoginGet(tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tmpl.ExecuteTemplate(w, "auth_login.html", map[string]any{"User": User(r), "CSRF": CSRF(r)})
}
}
func LoginPost(users UserStore, secret []byte, tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
username := strings.TrimSpace(r.FormValue("username"))
password := r.FormValue("password")
if !users.Verify(username, password) {
tmpl.ExecuteTemplate(w, "auth_login.html", map[string]any{
"Error": "Invalid username or password.",
"User": User(r),
"CSRF": CSRF(r),
})
return
}
setAuthCookie(w, username, secret)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
func Logout() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
clearAuthCookie(w)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
// ----- Setup (first-run) -----
func SetupGet(tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tmpl.ExecuteTemplate(w, "auth_setup.html", map[string]any{"User": User(r), "CSRF": CSRF(r)})
}
}
func SetupPost(users UserStore, usersPath string, secret []byte, tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
username := strings.TrimSpace(r.FormValue("username"))
password := r.FormValue("password")
confirm := r.FormValue("confirm")
if password != confirm {
tmpl.ExecuteTemplate(w, "auth_setup.html", map[string]any{"Error": "Passwords do not match.", "User": User(r), "CSRF": CSRF(r)})
return
}
if err := users.Add(username, password); err != nil {
tmpl.ExecuteTemplate(w, "auth_setup.html", map[string]any{"Error": err.Error(), "User": User(r), "CSRF": CSRF(r)})
return
}
// Save SSH key if provided.
if key := strings.TrimSpace(r.FormValue("ssh_key")); key != "" {
users.SetSSHKeys(username, []string{key})
}
if err := users.Save(usersPath); err != nil {
tmpl.ExecuteTemplate(w, "auth_setup.html", map[string]any{"Error": "Failed to save: " + err.Error(), "User": User(r), "CSRF": CSRF(r)})
return
}
setAuthCookie(w, username, secret)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
// ----- User management -----
func UsersGet(users UserStore, usersPath string, tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if User(r) == "" {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
userList := make([]string, 0, len(users))
for u := range users {
userList = append(userList, u)
}
sshKeys := users[User(r)].SSHKeys
tmpl.ExecuteTemplate(w, "auth_users.html", map[string]any{
"Users": userList,
"User": User(r),
"SSHKeys": sshKeys,
"CSRF": CSRF(r),
})
}
}
func UsersPost(users UserStore, usersPath string, tmpl *template.Template) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if User(r) == "" {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
action := r.FormValue("action")
switch action {
case "add":
username := strings.TrimSpace(r.FormValue("username"))
password := r.FormValue("password")
if err := users.Add(username, password); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
users.Save(usersPath)
case "remove":
username := strings.TrimSpace(r.FormValue("username"))
if username == User(r) {
http.Error(w, "cannot remove yourself", http.StatusBadRequest)
return
}
users.Remove(username)
users.Save(usersPath)
case "change-password":
currentUser := User(r)
newPassword := r.FormValue("password")
if err := users.ChangePassword(currentUser, newPassword); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
users.Save(usersPath)
case "add-ssh-key":
currentUser := User(r)
if key := strings.TrimSpace(r.FormValue("ssh_key")); key != "" {
info := users[currentUser]
info.SSHKeys = append(info.SSHKeys, key)
users[currentUser] = info
users.Save(usersPath)
}
case "remove-ssh-key":
currentUser := User(r)
idx := r.FormValue("index")
if info, ok := users[currentUser]; ok {
var i int
fmt.Sscanf(idx, "%d", &i)
if i >= 0 && i < len(info.SSHKeys) {
info.SSHKeys = append(info.SSHKeys[:i], info.SSHKeys[i+1:]...)
users[currentUser] = info
users.Save(usersPath)
}
}
}
http.Redirect(w, r, "/users", http.StatusSeeOther)
}
}