This commit is contained in:
hero920103 committed 2026-10-19 01:13:17 +08:00
1 parent 5718a00e31
commit 1850ac640e
226 files changed
+37772 -12071

No files matched your search

+33 -30
View File
@@ -7,7 +7,7 @@ import (
"strings"
"server/models"
"server/pkg/jwtutil"
"server/pkg/identity"
beego "github.com/beego/beego/v2/server/web"
)
@@ -111,29 +111,20 @@ func sanitizeTenantRights(v interface{}) *string {
return &s
}
// tenantRoleReadOnly 判定角色对租户端是否只读。
// is_locked 是平台下发的显式开关;tenant_id==0 的全局角色不属于任何租户,天然只读。
// 两者取并集,避免平台建全局角色时漏设 is_locked 就把写权限漏给租户。
func tenantRoleReadOnly(role models.AdminRole) bool {
return role.IsLocked == 1 || role.TenantID == 0
}
// currentTenantID 从 Bearer Token 解析当前登录租户 ID。
// 返回 (tid, true) 表示成功;失败时已写入 401/403 响应并返回 (0, false)。
func (c *BackendRoleController) currentTenantID() (uint64, bool) {
authHeader := c.Ctx.Request.Header.Get("Authorization")
if authHeader == "" {
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "未登录"}
_ = c.ServeJSON()
return 0, false
}
authParts := strings.SplitN(authHeader, " ", 2)
if len(authParts) != 2 || authParts[0] != "Bearer" {
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "认证信息格式错误"}
_ = c.ServeJSON()
return 0, false
}
claims, err := jwtutil.ParseToken(authParts[1])
claims, err := identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndBackend, identity.EndApp)
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 401, "msg": "无效的token"}
_ = c.ServeJSON()
return 0, false
}
if claims.UserType != "backend" && claims.UserType != "app" {
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "无权访问"}
code := identity.Status(err)
c.Data["json"] = map[string]interface{}{"code": code, "msg": err.Error()}
_ = c.ServeJSON()
return 0, false
}
@@ -297,13 +288,13 @@ func (c *BackendRoleController) UpdateRole() {
return
}
// 全局角色(tenant_id=0)由平台端统一维护,租户端只读
// 平台下发或全局共享的角色由平台端统一维护,租户端只读
var target models.AdminRole
if e := models.Orm.QueryTable(new(models.AdminRole)).
Filter("id", id).
Filter("cid", 2).
One(&target); e == nil && target.TenantID == 0 {
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "全局角色由平台端统一维护,租户端不能修改"}
One(&target); e == nil && tenantRoleReadOnly(target) {
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "该角色由平台端统一下发,租户端不能修改"}
_ = c.ServeJSON()
return
}
@@ -328,22 +319,34 @@ func (c *BackendRoleController) UpdateRole() {
}
}
// 仅允许更新本租户名下的角色,防止越权改到其他租户
cnt, err := models.Orm.QueryTable(new(models.AdminRole)).
// 仅允许更新本租户名下的角色,防止越权改到其他租户。
// 先查存在性再更新:MySQL 的 affected rows 只算「真正变化」的行,
// 提交与库里完全一致的值会返回 0,直接用它判 404 会把「保存成功但没改动」误报成「角色不存在」。
exist, err := models.Orm.QueryTable(new(models.AdminRole)).
Filter("id", id).
Filter("cid", 2).
Filter("tenant_id", tid).
Update(update)
Count()
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "更新失败"}
_ = c.ServeJSON()
return
}
if cnt == 0 {
if exist == 0 {
c.Data["json"] = map[string]interface{}{"code": 404, "msg": "角色不存在"}
_ = c.ServeJSON()
return
}
if _, err := models.Orm.QueryTable(new(models.AdminRole)).
Filter("id", id).
Filter("cid", 2).
Filter("tenant_id", tid).
Update(update); err != nil {
c.Data["json"] = map[string]interface{}{"code": 500, "msg": "更新失败"}
_ = c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"}
_ = c.ServeJSON()
}
@@ -362,13 +365,13 @@ func (c *BackendRoleController) DeleteRole() {
_ = c.ServeJSON()
return
}
// 全局角色(tenant_id=0)由平台端统一维护,租户端不能删除
// 平台下发或全局共享的角色由平台端统一维护,租户端不能删除
var target models.AdminRole
if e := models.Orm.QueryTable(new(models.AdminRole)).
Filter("id", id).
Filter("cid", 2).
One(&target); e == nil && target.TenantID == 0 {
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "全局角色由平台端统一维护,租户端不能删除"}
One(&target); e == nil && tenantRoleReadOnly(target) {
c.Data["json"] = map[string]interface{}{"code": 403, "msg": "该角色由平台端统一下发,租户端不能删除"}
_ = c.ServeJSON()
return
}