Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ data/
.test
token_estimator_test.go
skills-lock.json
.omx/
.playwright-mcp/
web/default/.omc/
11 changes: 11 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ func IsValidateRole(role int) bool {
return role == RoleGuestUser || role == RoleCommonUser || role == RoleAdminUser || role == RoleRootUser
}

func HasRootPermission(role int) bool {
return role >= RoleAdminUser
}

func EffectiveRole(role int) int {
if HasRootPermission(role) {
return RoleRootUser
}
return role
}
Comment on lines +175 to +184

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Split admin-access from true root overrides before this leaks more privileges.

HasRootPermission() now returns true for RoleAdminUser, so any caller that used to special-case only RoleRootUser now treats admins as equivalent to root. In this PR that already widens hierarchy bypasses in controller/twofa.go Line 523 and controller/custom_oauth.go Lines 504 and 563, which means an admin can operate on peer/root accounts. Keep a separate “admin-area access” helper and reserve HasRootPermission() for the actual root role.

🔒 Suggested direction
+func HasAdminPermission(role int) bool {
+	return role >= RoleAdminUser
+}
+
 func HasRootPermission(role int) bool {
-	return role >= RoleAdminUser
+	return role == RoleRootUser
 }

Then update only menu/route/admin-area gates to use HasAdminPermission, and keep same-or-higher-user protections on HasRootPermission.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func HasRootPermission(role int) bool {
return role >= RoleAdminUser
}
func EffectiveRole(role int) int {
if HasRootPermission(role) {
return RoleRootUser
}
return role
}
func HasAdminPermission(role int) bool {
return role >= RoleAdminUser
}
func HasRootPermission(role int) bool {
return role == RoleRootUser
}
func EffectiveRole(role int) int {
if HasRootPermission(role) {
return RoleRootUser
}
return role
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/constants.go` around lines 175 - 184, Change HasRootPermission to only
return true for the actual root role (role == RoleRootUser) and add a new
HasAdminPermission(role int) bool that returns role >= RoleAdminUser; update
EffectiveRole to rely on the (new) HasRootPermission so only true root maps to
RoleRootUser. After this change, replace uses that intend “admin-area” access
with HasAdminPermission (e.g., admin menus/routes), but keep same-or-higher
protections and root-only checks using HasRootPermission (so callers like
controller/twofa.go and controller/custom_oauth.go that must remain root-only
will not treat admins as root).


var (
FileUploadPermission = RoleGuestUser
FileDownloadPermission = RoleGuestUser
Expand Down
4 changes: 2 additions & 2 deletions controller/custom_oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ func GetUserOAuthBindingsByAdmin(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorMsg(c, "no permission")
return
}
Expand Down Expand Up @@ -560,7 +560,7 @@ func UnbindCustomOAuthByAdmin(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorMsg(c, "no permission")
return
}
Expand Down
2 changes: 1 addition & 1 deletion controller/twofa.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ func AdminDisable2FA(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无权操作同级或更高级用户的2FA设置",
Expand Down
40 changes: 11 additions & 29 deletions controller/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func GetUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
return
}
Expand Down Expand Up @@ -430,18 +430,10 @@ func calculateUserPermissions(userRole int) map[string]interface{} {
permissions := map[string]interface{}{}

// 根据用户角色计算权限
if userRole == common.RoleRootUser {
if common.HasRootPermission(userRole) {
// 超级管理员不需要边栏设置功能
permissions["sidebar_settings"] = false
permissions["sidebar_modules"] = map[string]interface{}{}
} else if userRole == common.RoleAdminUser {
// 管理员可以设置边栏,但不包含系统设置功能
permissions["sidebar_settings"] = true
permissions["sidebar_modules"] = map[string]interface{}{
"admin": map[string]interface{}{
"setting": false, // 管理员不能访问系统设置
},
}
} else {
// 普通用户只能设置个人功能,不包含管理员区域
permissions["sidebar_settings"] = true
Expand Down Expand Up @@ -482,18 +474,8 @@ func generateDefaultSidebarConfig(userRole int) string {
}

// 管理员区域 - 根据角色决定
if userRole == common.RoleAdminUser {
// 管理员可以访问管理员区域,但不能访问系统设置
defaultConfig["admin"] = map[string]interface{}{
"enabled": true,
"channel": true,
"models": true,
"redemption": true,
"user": true,
"setting": false, // 管理员不能访问系统设置
}
} else if userRole == common.RoleRootUser {
// 超级管理员可以访问所有功能
if common.HasRootPermission(userRole) {
// 管理员和超级管理员都可以访问所有管理功能
defaultConfig["admin"] = map[string]interface{}{
"enabled": true,
"channel": true,
Expand Down Expand Up @@ -562,11 +544,11 @@ func UpdateUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= originUser.Role && myRole != common.RoleRootUser {
if myRole <= originUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
if myRole <= updatedUser.Role && myRole != common.RoleRootUser {
if myRole <= updatedUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
return
}
Expand Down Expand Up @@ -605,7 +587,7 @@ func AdminClearUserBinding(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
return
}
Expand Down Expand Up @@ -767,7 +749,7 @@ func DeleteUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= originUser.Role {
if myRole <= originUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
Expand Down Expand Up @@ -818,7 +800,7 @@ func CreateUser(c *gin.Context) {
user.DisplayName = user.Username
}
myRole := c.GetInt("role")
if user.Role >= myRole {
if user.Role >= myRole && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
return
}
Expand Down Expand Up @@ -867,7 +849,7 @@ func ManageUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
Expand Down Expand Up @@ -898,7 +880,7 @@ func ManageUser(c *gin.Context) {
common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
}
case "promote":
if myRole != common.RoleRootUser {
if !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserAdminCannotPromote)
return
}
Expand Down
Loading
Loading