nex_math/backend/seed/rbac.py

112 lines
3.8 KiB
Python

"""RBAC 种子:权限、角色与默认账号。"""
from __future__ import annotations
from sqlalchemy.orm import Session
from models import Permission, Role, RolePermission, User, UserRole
from services.security import hash_password
PERMISSIONS = [
("learning:use", "使用学习功能", "learning"),
("users:manage", "用户管理", "admin"),
("catalog:manage", "教材/课程管理", "admin"),
("question-bank:manage", "题库管理", "admin"),
("llm:manage", "模型配置管理", "admin"),
("knowledge:manage", "知识图谱管理", "admin"),
]
ROLES = {
"admin": {
"name": "管理员",
"description": "拥有用户、模型、教材课程与题库的全部管理权限",
# 管理员是纯管理账号,不参与学习,因此不授予 learning:use
"permissions": [
code for code, _, _ in PERMISSIONS if code != "learning:use"
],
},
"student": {
"name": "普通用户",
"description": "使用学习总览、练习、错题、分析等学习功能",
"permissions": ["learning:use"],
},
}
DEFAULT_USERS = [
("admin", "admin123", "系统管理员", "admin"),
("student", "student123", "演示学生", "student"),
]
def _permission_map(db: Session) -> dict[str, Permission]:
rows = {row.code: row for row in db.query(Permission).all()}
for code, name, group in PERMISSIONS:
if code not in rows:
row = Permission(code=code, name=name, group=group)
db.add(row)
db.flush()
rows[code] = row
return rows
def ensure_rbac(db: Session) -> None:
"""幂等创建权限、角色与默认账号,并同步旧库中已过期的角色权限。"""
permissions = _permission_map(db)
roles = {row.code: row for row in db.query(Role).all()}
for code, meta in ROLES.items():
if code not in roles:
role = Role(code=code, name=meta["name"], description=meta["description"])
db.add(role)
db.flush()
roles[code] = role
expected = set(meta["permissions"])
existing_rows = (
db.query(RolePermission, Permission.code)
.join(Permission, Permission.id == RolePermission.permission_id)
.filter(RolePermission.role_id == roles[code].id)
.all()
)
for role_permission, permission_code in existing_rows:
if permission_code not in expected:
db.delete(role_permission)
for permission_code in meta["permissions"]:
exists = (
db.query(RolePermission)
.filter(
RolePermission.role_id == roles[code].id,
RolePermission.permission_id == permissions[permission_code].id,
)
.first()
)
if exists is None:
db.add(
RolePermission(
role_id=roles[code].id,
permission_id=permissions[permission_code].id,
)
)
db.flush()
for username, password, display_name, role_code in DEFAULT_USERS:
user = db.query(User).filter(User.username == username).first()
if user is None:
user = User(
username=username,
password_hash=hash_password(password),
display_name=display_name,
active=True,
)
db.add(user)
db.flush()
assignment = (
db.query(UserRole)
.filter(
UserRole.user_id == user.id,
UserRole.role_id == roles[role_code].id,
)
.first()
)
if assignment is None:
db.add(UserRole(user_id=user.id, role_id=roles[role_code].id))
db.commit()