New Sign in

fierj

Public

Tiny personal git forge

← fierj / auth_handlers.go
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)
	}
}