New Sign in

fierj

Public

Tiny personal git forge

← fierj / auth_handlers.go
package main

import (
	"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)})
	}
}

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),
			})
			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)})
	}
}

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)})
			return
		}
		if err := users.Add(username, password); err != nil {
			tmpl.ExecuteTemplate(w, "auth_setup.html", map[string]any{"Error": err.Error(), "User": User(r)})
			return
		}
		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)})
			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)
		}
		tmpl.ExecuteTemplate(w, "auth_users.html", map[string]any{
			"Users": userList,
			"User":  User(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)
		}
		http.Redirect(w, r, "/users", http.StatusSeeOther)
	}
}