mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-12 14:21:01 +00:00
feat(auth): block panel with default admin/admin credentials and guide credential change
checkLogin middleware now detects default admin/admin credentials and redirects every panel route to /panel/settings until they are changed. The settings page auto-opens the Authentication tab, shows a non-dismissible error banner, and lists 'Default credentials' first in the security checklist. Login response includes mustChangeCredentials so the login page can redirect directly. Logout is now POST-only. Password must be at least 10 characters and cannot be admin/admin.
This commit is contained in:
+14
-2
@@ -39,7 +39,7 @@ func NewIndexController(g *gin.RouterGroup) *IndexController {
|
||||
// initRouter sets up the routes for index, login, logout, and two-factor authentication.
|
||||
func (a *IndexController) initRouter(g *gin.RouterGroup) {
|
||||
g.GET("/", a.index)
|
||||
g.GET("/logout", a.logout)
|
||||
g.GET("/logout", a.logoutGet)
|
||||
// Public CSRF endpoint — the SPA login page (served by Vite in
|
||||
// dev or by serveDistPage in prod) needs a token to POST /login,
|
||||
// but the panel-side /panel/csrf-token sits behind checkLogin.
|
||||
@@ -48,6 +48,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) {
|
||||
g.GET("/csrf-token", a.csrfToken)
|
||||
|
||||
g.POST("/login", middleware.CSRFMiddleware(), a.login)
|
||||
g.POST("/logout", middleware.CSRFMiddleware(), a.logout)
|
||||
g.POST("/getTwoFactorEnable", middleware.CSRFMiddleware(), a.getTwoFactorEnable)
|
||||
}
|
||||
|
||||
@@ -130,7 +131,9 @@ func (a *IndexController) login(c *gin.Context) {
|
||||
}
|
||||
|
||||
logger.Infof("%s logged in successfully", safeUser)
|
||||
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), nil)
|
||||
jsonMsgObj(c, I18nWeb(c, "pages.login.toasts.successLogin"), gin.H{
|
||||
"mustChangeCredentials": user.Username == "admin" && form.Password == "admin",
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func loginFailureReason(err error) string {
|
||||
@@ -150,9 +153,18 @@ func (a *IndexController) logout(c *gin.Context) {
|
||||
logger.Warning("Unable to clear session on logout:", err)
|
||||
}
|
||||
c.Header("Cache-Control", "no-store")
|
||||
if isAjax(c) {
|
||||
jsonMsg(c, "", nil)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||
}
|
||||
|
||||
func (a *IndexController) logoutGet(c *gin.Context) {
|
||||
c.Header("Allow", http.MethodPost)
|
||||
c.AbortWithStatus(http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
// csrfToken returns the session CSRF token. Public — the login page
|
||||
// needs a token before authenticating.
|
||||
func (a *IndexController) csrfToken(c *gin.Context) {
|
||||
|
||||
Reference in New Issue
Block a user