Compare commits

3 Commits

Author SHA1 Message Date
dxc
5beb373677 fix: standardize operator role to uppercase for permission checks
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
2026-02-27 17:11:29 +08:00
dxc
c1e4acc1d8 fix: standardize role case handling in permission logic
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
2026-02-27 17:07:45 +08:00
dxc
a0993767fe fix: make SUPER_ADMIN role checks case-insensitive across app
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
2026-02-27 17:04:22 +08:00
11 changed files with 62 additions and 37 deletions

View File

@ -19,8 +19,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
return ['system_user:*'] return ['system_user:*']
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
# 合并菜单和元素权限 # 合并菜单和元素权限

View File

@ -22,8 +22,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
return ['bom_manage:*'] return ['bom_manage:*']
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
# 合并菜单和元素权限 # 合并菜单和元素权限

View File

@ -20,8 +20,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
# 返回所有以 inbound_buy: 开头的权限码(这里我们返回一个特殊标记,表示全部) # 返回所有以 inbound_buy: 开头的权限码(这里我们返回一个特殊标记,表示全部)
# 为了简单,我们返回 ['inbound_buy:*'],在过滤函数中特殊处理 # 为了简单,我们返回 ['inbound_buy:*'],在过滤函数中特殊处理
return ['inbound_buy:*'] return ['inbound_buy:*']

View File

@ -21,8 +21,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
# 返回所有以 inbound_product: 开头的权限码(这里我们返回一个特殊标记,表示全部) # 返回所有以 inbound_product: 开头的权限码(这里我们返回一个特殊标记,表示全部)
# 为了简单,我们返回 ['inbound_product:*'],在过滤函数中特殊处理 # 为了简单,我们返回 ['inbound_product:*'],在过滤函数中特殊处理
return ['inbound_product:*'] return ['inbound_product:*']

View File

@ -22,8 +22,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
# 返回所有以 inbound_semi: 开头的权限码(这里我们返回一个特殊标记,表示全部) # 返回所有以 inbound_semi: 开头的权限码(这里我们返回一个特殊标记,表示全部)
# 为了简单,我们返回 ['inbound_semi:*'],在过滤函数中特殊处理 # 为了简单,我们返回 ['inbound_semi:*'],在过滤函数中特殊处理
return ['inbound_semi:*'] return ['inbound_semi:*']

View File

@ -21,8 +21,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
return ['inbound_service:*'] return ['inbound_service:*']
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
# 合并菜单和元素权限 # 合并菜单和元素权限

View File

@ -22,8 +22,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
return ['outbound_list:*'] return ['outbound_list:*']
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
# 合并菜单和元素权限 # 合并菜单和元素权限
@ -115,7 +115,7 @@ def create_outbound():
return jsonify({'code': 403, 'msg': '未授权'}), 403 return jsonify({'code': 403, 'msg': '未授权'}), 403
# 超级管理员直接放行 # 超级管理员直接放行
if user_role != 'super_admin': if user_role.upper() != 'SUPER_ADMIN':
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
perms = perm_dict.get('menus', []) + perm_dict.get('elements', []) perms = perm_dict.get('menus', []) + perm_dict.get('elements', [])
if ('outbound_create:operation' not in perms) and ('outbound_selection:operation' not in perms): if ('outbound_create:operation' not in perms) and ('outbound_selection:operation' not in perms):

View File

@ -20,8 +20,8 @@ def get_current_user_permissions():
user_role = claims.get('role') user_role = claims.get('role')
if not user_role: if not user_role:
return [] return []
# 超级管理员返回所有字段权限 # 超级管理员返回所有字段权限 (忽略大小写)
if user_role == 'super_admin': if user_role.upper() == 'SUPER_ADMIN':
return ['*'] return ['*']
perm_dict = AuthService.get_user_permissions(user_role) perm_dict = AuthService.get_user_permissions(user_role)
# 合并菜单和元素权限 # 合并菜单和元素权限

View File

@ -1,6 +1,7 @@
# app/services/auth_service.py # app/services/auth_service.py
from app.models.system import SysUser, SysRolePermission # <== 引入 SysRolePermission from app.models.system import SysUser, SysRolePermission # <== 引入 SysRolePermission
from app.extensions import db from app.extensions import db
from sqlalchemy import func
from flask_jwt_extended import create_access_token from flask_jwt_extended import create_access_token
from app.utils.constants import UserRole from app.utils.constants import UserRole
from datetime import timedelta from datetime import timedelta
@ -51,9 +52,10 @@ class AuthService:
if user.status != 'active': if user.status != 'active':
raise ValueError("账号已被禁用,请联系管理员") raise ValueError("账号已被禁用,请联系管理员")
user_role = user.role user_role = user.role.upper() if user.role else None
user_id = user.id user_id = user.id
user_info = user.to_dict() user_info = user.to_dict()
user_info['role'] = user_role
# 3. 生成 Token # 3. 生成 Token
# Token 中 identity 存数据库IDclaims 存登录账号ID # Token 中 identity 存数据库IDclaims 存登录账号ID
@ -80,7 +82,9 @@ class AuthService:
创建新用户 创建新用户
data 包含: cn_name(张三), username(zhangsan), ... data 包含: cn_name(张三), username(zhangsan), ...
""" """
if operator_role not in [UserRole.SUPER_ADMIN, UserRole.SUPERVISOR]: # 标准化操作者角色为全大写
operator_role_upper = operator_role.upper() if operator_role else None
if operator_role_upper not in [UserRole.SUPER_ADMIN, UserRole.SUPERVISOR]:
raise Exception("权限不足:只有超级管理员或主管可以创建新用户") raise Exception("权限不足:只有超级管理员或主管可以创建新用户")
cn_name = data.get('cn_name') cn_name = data.get('cn_name')
@ -89,7 +93,8 @@ class AuthService:
if not cn_name or not pinyin_base: if not cn_name or not pinyin_base:
raise Exception("姓名和账号不能为空") raise Exception("姓名和账号不能为空")
role = data.get('role') role_raw = data.get('role')
role = role_raw.upper() if role_raw else None
# 验证角色合法性 # 验证角色合法性
valid_roles = [ valid_roles = [
@ -100,7 +105,7 @@ class AuthService:
if role not in valid_roles: if role not in valid_roles:
raise Exception(f"角色无效") raise Exception(f"角色无效")
if operator_role == UserRole.SUPERVISOR and role == UserRole.SUPER_ADMIN: if operator_role_upper == UserRole.SUPERVISOR and role == UserRole.SUPER_ADMIN:
raise Exception("权限不足:主管无法创建超级管理员") raise Exception("权限不足:主管无法创建超级管理员")
email = data.get('email', '') email = data.get('email', '')
@ -149,7 +154,9 @@ class AuthService:
更新用户信息 更新用户信息
注意: 这里暂时不允许修改用户名/账号,因为涉及 split 逻辑较复杂,且通常账号不开通后不改 注意: 这里暂时不允许修改用户名/账号,因为涉及 split 逻辑较复杂,且通常账号不开通后不改
""" """
if operator_role not in [UserRole.SUPER_ADMIN, UserRole.SUPERVISOR]: # 标准化操作者角色为全大写
operator_role_upper = operator_role.upper() if operator_role else None
if operator_role_upper not in [UserRole.SUPER_ADMIN, UserRole.SUPERVISOR]:
raise Exception("权限不足") raise Exception("权限不足")
user = SysUser.query.get(user_id) user = SysUser.query.get(user_id)
@ -162,10 +169,11 @@ class AuthService:
v for k, v in UserRole.__dict__.items() v for k, v in UserRole.__dict__.items()
if not k.startswith('__') and isinstance(v, str) if not k.startswith('__') and isinstance(v, str)
] ]
new_role = data['role'] new_role_raw = data['role']
new_role = new_role_raw.upper() if new_role_raw else None
if new_role not in valid_roles: if new_role not in valid_roles:
raise Exception(f"角色无效") raise Exception(f"角色无效")
if operator_role == UserRole.SUPERVISOR and new_role == UserRole.SUPER_ADMIN: if operator_role_upper == UserRole.SUPERVISOR and new_role == UserRole.SUPER_ADMIN:
raise Exception("权限不足") raise Exception("权限不足")
user.role = new_role user.role = new_role
@ -201,7 +209,9 @@ class AuthService:
@staticmethod @staticmethod
def delete_user(user_id, operator_role): def delete_user(user_id, operator_role):
"""删除用户""" """删除用户"""
if operator_role != UserRole.SUPER_ADMIN: # 标准化操作者角色为全大写
operator_role_upper = operator_role.upper() if operator_role else None
if operator_role_upper != UserRole.SUPER_ADMIN:
raise Exception("权限不足:只有超级管理员可以删除用户") raise Exception("权限不足:只有超级管理员可以删除用户")
user = SysUser.query.get(user_id) user = SysUser.query.get(user_id)
@ -221,18 +231,27 @@ class AuthService:
'elements': ['inbound_buy:unit_price', ...] 'elements': ['inbound_buy:unit_price', ...]
} }
""" """
# 超级管理员返回所有权限(通配符)
from app.utils.constants import UserRole
if role_code and role_code.upper() == UserRole.SUPER_ADMIN:
# 返回通配符,表示拥有所有菜单和元素权限
return {
'menus': ['*'],
'elements': ['*']
}
# 1. 查菜单权限 # 1. 查菜单权限
menu_perms = SysRolePermission.query.filter_by( menu_perms = SysRolePermission.query.filter(
role_code=role_code, func.upper(SysRolePermission.role_code) == role_code.upper(),
type='menu' SysRolePermission.type == 'menu'
).all() ).all()
menu_codes = [p.target_code for p in menu_perms] menu_codes = [p.target_code for p in menu_perms]
# 2. 查元素(列)权限 # 2. 查元素(列)权限
# 注意:这里我们只返回用户拥有的。前端逻辑是:"如果列配置了Key且用户没这个Key则隐藏" # 注意:这里我们只返回用户拥有的。前端逻辑是:"如果列配置了Key且用户没这个Key则隐藏"
element_perms = SysRolePermission.query.filter_by( element_perms = SysRolePermission.query.filter(
role_code=role_code, func.upper(SysRolePermission.role_code) == role_code.upper(),
type='element' SysRolePermission.type == 'element'
).all() ).all()
# 这里的 target_code 就是列的 code (如 unit_price) # 这里的 target_code 就是列的 code (如 unit_price)
@ -243,4 +262,4 @@ class AuthService:
return { return {
'menus': menu_codes, 'menus': menu_codes,
'elements': element_codes 'elements': element_codes
} }

View File

@ -16,12 +16,13 @@ def role_required(*roles):
def decorator(*args, **kwargs): def decorator(*args, **kwargs):
claims = get_jwt() claims = get_jwt()
user_role = claims.get('role') user_role = claims.get('role')
user_role_upper = user_role.upper() if user_role else None
# 如果是超级管理员,拥有上帝视角,直接放行 (可选) # 如果是超级管理员,拥有上帝视角,直接放行 (可选)
if user_role == 'super_admin': if user_role_upper == 'SUPER_ADMIN':
return fn(*args, **kwargs) return fn(*args, **kwargs)
if user_role not in roles: if user_role_upper not in [r.upper() for r in roles]:
return jsonify(msg='权限不足:您没有访问此资源的权限'), 403 return jsonify(msg='权限不足:您没有访问此资源的权限'), 403
return fn(*args, **kwargs) return fn(*args, **kwargs)
@ -63,8 +64,8 @@ def permission_required(permission_code):
claims = get_jwt() claims = get_jwt()
user_role = claims.get('role') user_role = claims.get('role')
# 超级管理员放行 # 超级管理员放行 (忽略大小写)
if user_role == 'super_admin': if user_role and user_role.upper() == 'SUPER_ADMIN':
return fn(*args, **kwargs) return fn(*args, **kwargs)
# 根据角色查询数据库中的权限 # 根据角色查询数据库中的权限

View File

@ -35,7 +35,8 @@ export const useUserStore = defineStore('user', () => {
// 处理用户信息 (确保后端返回结构中有 user 字段) // 处理用户信息 (确保后端返回结构中有 user 字段)
if (data.user) { if (data.user) {
role.value = data.user.role || 'user' // 默认给个 user 角色防止空 const rawRole = data.user.role || 'user'
role.value = rawRole.toUpperCase() // 角色统一转换为大写
username.value = data.user.username || '用户' username.value = data.user.username || '用户'
// 持久化存储用户信息 // 持久化存储用户信息
@ -114,6 +115,10 @@ export const useUserStore = defineStore('user', () => {
// 判断当前用户是否拥有某个权限(菜单或元素) // 判断当前用户是否拥有某个权限(菜单或元素)
const hasPermission = (code: string) => { const hasPermission = (code: string) => {
// 超级管理员拥有所有权限
if (role.value && role.value.toUpperCase() === 'SUPER_ADMIN') {
return true
}
return permissions.value.includes(code) return permissions.value.includes(code)
} }