fix: stabilize console scrolling and protect administrator login
This commit is contained in:
@@ -4,11 +4,14 @@ import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -64,6 +67,12 @@ func RegisterConsole(server *http.Server, authStores ...consoleauth.Store) {
|
||||
writeConsoleJSON(w, http.StatusOK, map[string]any{"authenticated": true})
|
||||
})
|
||||
mux.HandleFunc("POST /console/auth/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := http.NewCrossOriginProtection().Check(r); err != nil {
|
||||
writeConsoleJSON(w, http.StatusForbidden, map[string]any{"error": map[string]string{"code": "PERMISSION_DENIED", "message": "不允许跨站登录请求"}})
|
||||
return
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 8<<10)
|
||||
|
||||
if consoleAuth == nil {
|
||||
writeConsoleJSON(w, http.StatusServiceUnavailable, map[string]any{"error": map[string]string{"code": "UNAVAILABLE", "message": "console authentication is not configured"}})
|
||||
return
|
||||
@@ -78,10 +87,23 @@ func RegisterConsole(server *http.Server, authStores ...consoleauth.Store) {
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 8*time.Second)
|
||||
defer cancel()
|
||||
login, err := consoleAuth.Login(ctx, in.Username, in.Password, r.UserAgent(), remoteAddress(r))
|
||||
login, err := consoleAuth.Login(ctx, in.Username, in.Password, r.UserAgent(), loginRemoteAddress(r, consoleAuth))
|
||||
if err != nil {
|
||||
// 账号不存在、密码错误、账号停用和绑定凭据失效统一返回,避免账号枚举。
|
||||
writeConsoleJSON(w, http.StatusUnauthorized, map[string]any{"error": map[string]string{"code": "UNAUTHENTICATED", "message": "用户名或密码错误"}})
|
||||
var blocked *consoleauth.LoginBlocked
|
||||
if errors.As(err, &blocked) {
|
||||
seconds := max(1, int(math.Ceil(blocked.RetryAfter.Seconds())))
|
||||
code, message := "LOGIN_RATE_LIMITED", "登录尝试过于频繁,请稍后重试"
|
||||
if blocked.Locked {
|
||||
code, message = "LOGIN_LOCKED", "连续登录失败,已临时锁定,请稍后重试"
|
||||
}
|
||||
w.Header().Set("Retry-After", strconv.Itoa(seconds))
|
||||
writeConsoleJSON(w, http.StatusTooManyRequests, map[string]any{"error": map[string]any{"code": code, "message": message, "retry_after_seconds": seconds}})
|
||||
} else if errors.Is(err, fault.Unauthorized) {
|
||||
writeConsoleJSON(w, http.StatusUnauthorized, map[string]any{"error": map[string]string{"code": "UNAUTHENTICATED", "message": "用户名或密码错误"}})
|
||||
} else {
|
||||
// 存储或审计不可用时不发 Cookie,不将基础设施故障伪装成密码错误。
|
||||
writeConsoleJSON(w, http.StatusServiceUnavailable, map[string]any{"error": map[string]string{"code": "UNAVAILABLE", "message": "登录服务暂不可用,请稍后重试"}})
|
||||
}
|
||||
return
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{Name: consoleSessionCookie, Value: login.Token, Path: "/", HttpOnly: true, Secure: r.TLS != nil, SameSite: http.SameSiteLaxMode, Expires: login.ExpiresAt, MaxAge: int(time.Until(login.ExpiresAt).Seconds())})
|
||||
|
||||
Reference in New Issue
Block a user