caddydash/api/auth.go
2025-06-25 20:02:53 +08:00

216 lines
5.2 KiB
Go

package api
import (
"caddydash/config"
"caddydash/db"
"caddydash/user"
"net/http"
"strings"
"github.com/fenthope/sessions"
"github.com/infinite-iroha/touka"
)
var (
exactMatchPaths = map[string]struct{}{
"/login": {},
"/login.html": {},
"/v0/api/auth/login": {},
"/v0/api/auth/logout": {},
"/v0/api/auth/init": {},
"/init.html": {},
"/favicon.ico": {},
}
prefixMatchPaths = []string{ // 保持前缀匹配,因为数量少
"/js/",
"/css/",
}
loginMatchPaths = map[string]struct{}{
"/login": {},
"/login.html": {},
"/v0/api/auth/login": {},
}
initMatchPaths = map[string]struct{}{
"/v0/api/auth/init": {},
"/init.html": {},
}
)
func isPassPath(requestPath string) bool {
// 精确匹配
if _, ok := exactMatchPaths[requestPath]; ok {
return true
}
// 前缀匹配
for _, prefix := range prefixMatchPaths {
if strings.HasPrefix(requestPath, prefix) {
return true
}
}
return false
}
func isLoginPath(requestPath string) bool {
if _, ok := loginMatchPaths[requestPath]; ok {
return true
}
return false
}
func isInitPath(requestPath string) bool {
if _, ok := initMatchPaths[requestPath]; ok {
return true
}
return false
}
func SessionMiddleware(cdb *db.ConfigDB) touka.HandlerFunc {
return func(c *touka.Context) {
session := sessions.Default(c)
requestPath := c.Request.URL.Path
pass := isPassPath(requestPath)
if !user.IsAdminInit() && !pass || !user.IsAdminInit() && isLoginPath(requestPath) {
c.Redirect(http.StatusFound, "/init.html")
c.Abort()
return
} else if user.IsAdminInit() && isInitPath(requestPath) {
c.Redirect(http.StatusFound, "/login.html")
c.Abort()
return
}
if session.Get("authenticated") != true && !pass {
c.Redirect(http.StatusFound, "/login.html")
c.Abort()
return
}
c.Next()
}
}
func AuthLogin(c *touka.Context, cfg *config.Config, cdb *db.ConfigDB) {
username := c.PostForm("username")
password := c.PostForm("password")
// 输入验证
if username == "" || password == "" {
c.Errorf("Username or password not provided")
c.JSON(http.StatusBadRequest, touka.H{"error": "Need username and password"})
return
}
c.Infof("user login: %s password: %s", username, password)
// 验证账户密码
pass, err := user.CheckLogin(username, password, cdb)
if err != nil {
c.Errorf("Failed to check login: %v", err)
c.JSON(http.StatusInternalServerError, touka.H{"error": "Internal Auth Check Error"})
return
}
if !pass {
c.Errorf("Invalid username or password")
c.JSON(http.StatusUnauthorized, touka.H{"error": "Invalid username or password"})
return
}
session := sessions.Default(c)
session.Set("authenticated", true)
session.Save()
c.Infof("Login successful for user: %s", username)
c.JSON(http.StatusOK, touka.H{"success": true})
}
func AuthLogout(c *touka.Context) {
session := sessions.Default(c)
session.Set("authenticated", false)
session.Clear()
err := session.Save()
if err != nil {
c.Errorf("Failed to save session: %v", err)
c.JSON(http.StatusInternalServerError, touka.H{"error": "Failed to save session"})
return
}
c.Redirect(http.StatusFound, "/login.html")
}
func ResetPassword(cdb *db.ConfigDB) touka.HandlerFunc {
return func(c *touka.Context) {
username := c.PostForm("username")
oldPassword := c.PostForm("old_password")
newPassword := c.PostForm("new_password")
// 验证是否为空
if username == "" || oldPassword == "" || newPassword == "" {
c.JSON(400, touka.H{"error": "username and password are required"})
return
}
// 验证用户是否存在
exist, err := cdb.IsUserExists(username)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
if !exist {
//不正确的参数
c.JSON(400, touka.H{"error": "user not exist"})
return
}
// 是否可以重置
ok, err := user.CheckLogin(username, oldPassword, cdb)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
if !ok {
// 错误的密码
c.JSON(400, touka.H{"error": "current password is not correct"})
return
}
// 更新密码
hashpwd, err := user.HashPassword(newPassword)
if err != nil {
c.Errorf("Failed to hash password: %v", err)
c.JSON(500, touka.H{"error": err.Error()})
return
}
err = cdb.UpdateUserPassword(username, hashpwd)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
// 进行logout
AuthLogout(c)
}
}
func AuthInitHandle(cdb *db.ConfigDB) touka.HandlerFunc {
return func(c *touka.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
// 验证是否为空
if username == "" || password == "" {
c.JSON(400, touka.H{"error": "username and password are required"})
return
}
// 初始化管理员
err := user.InitAdminUser(username, password, cdb)
if err != nil {
c.JSON(500, touka.H{"error": err.Error()})
return
}
c.JSON(200, touka.H{"message": "admin initialized"})
}
}
func AuthInitStatus() touka.HandlerFunc {
return func(c *touka.Context) {
// 返回是否init管理员
isInit := user.IsAdminInit()
if isInit {
c.JSON(200, touka.H{"admin_init": true})
} else {
c.JSON(200, touka.H{"admin_init": false})
}
}
}