mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-15 09:06:07 +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:
+34
-1
@@ -4,8 +4,10 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/logger"
|
||||
"github.com/mhsanaei/3x-ui/v3/util/crypto"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/locale"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/session"
|
||||
|
||||
@@ -17,7 +19,8 @@ type BaseController struct{}
|
||||
|
||||
// checkLogin is a middleware that verifies user authentication and handles unauthorized access.
|
||||
func (a *BaseController) checkLogin(c *gin.Context) {
|
||||
if !session.IsLogin(c) {
|
||||
user := session.GetLoginUser(c)
|
||||
if user == nil {
|
||||
if isAjax(c) {
|
||||
pureJsonMsg(c, http.StatusUnauthorized, false, I18nWeb(c, "pages.login.loginAgain"))
|
||||
} else {
|
||||
@@ -25,11 +28,41 @@ func (a *BaseController) checkLogin(c *gin.Context) {
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||
}
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if isDefaultAdminCredential(user.Username, user.Password) && !credentialChangeRouteAllowed(c) {
|
||||
if isAjax(c) {
|
||||
pureJsonMsg(c, http.StatusForbidden, false, "Change the default admin credentials before continuing.")
|
||||
} else {
|
||||
c.Header("Cache-Control", "no-store")
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")+"panel/settings")
|
||||
}
|
||||
c.Abort()
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func isDefaultAdminCredential(username string, hashedPassword string) bool {
|
||||
return username == "admin" && crypto.CheckPasswordHash(hashedPassword, "admin")
|
||||
}
|
||||
|
||||
func credentialChangeRouteAllowed(c *gin.Context) bool {
|
||||
basePath := c.GetString("base_path")
|
||||
path := c.Request.URL.Path
|
||||
allowedPrefixes := []string{
|
||||
basePath + "panel/settings",
|
||||
basePath + "panel/setting/",
|
||||
basePath + "panel/csrf-token",
|
||||
}
|
||||
for _, prefix := range allowedPrefixes {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// I18nWeb retrieves an internationalized message for the web interface based on the current locale.
|
||||
func I18nWeb(c *gin.Context, name string, params ...string) string {
|
||||
anyfunc, funcExists := c.Get("I18n")
|
||||
|
||||
Reference in New Issue
Block a user