This commit is contained in:
Your Name
2026-08-07 17:51:57 +08:00
parent 6119fdd767
commit 327a0bc42f
29 changed files with 1610 additions and 579 deletions
+23 -12
View File
@@ -14,7 +14,7 @@ from .account_limits import (
normalize_max_accounts,
)
from .account_quota import default_stop_worker, sync_user_account_quota
from .dependencies import get_current_user, require_user_manager
from .dependencies import get_current_user, require_role_manager, require_user_manager
from .email_service import (
build_password_reset_link,
build_verification_link,
@@ -37,6 +37,7 @@ from .role_service import (
user_permission_payload,
)
from .roles import ROLE_OPERATOR, is_admin
from .scopes import ensure_user_manageable, users_for_manager
from .schemas import (
LoginRequest,
MessageResponse,
@@ -393,17 +394,20 @@ async def list_auth_roles(
db: AsyncSession = Depends(get_db),
_: User = Depends(get_current_user),
):
"""Lightweight role list for dropdowns (any logged-in user)."""
"""Lightweight role list for dropdowns (any logged-in user).
Intentionally omits permission arrays to avoid leaking the full ACL map.
"""
records = await list_role_records(db)
return RolesResponse(
roles=[
RoleInfo(
value=item.code,
label=item.label,
description=item.description or None,
description=None,
is_system=item.is_system,
is_admin=item.is_admin,
permissions=list(item.permissions),
permissions=[],
)
for item in records
]
@@ -417,9 +421,9 @@ roles_router = APIRouter(prefix="/api/roles", tags=["roles"])
@users_router.get("", response_model=list[UserResponse])
async def list_users(
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
current: User = Depends(require_user_manager),
):
result = await db.execute(select(User).order_by(User.id.asc()))
result = await db.execute(users_for_manager(current).order_by(User.id.asc()))
users = result.scalars().all()
responses = []
for user in users:
@@ -431,13 +435,15 @@ async def list_users(
async def create_user(
body: UserCreate,
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
current: User = Depends(require_user_manager),
):
settings = await load_settings(db)
exists = await db.execute(select(User).where(User.username == body.username))
if exists.scalar_one_or_none():
raise HTTPException(status_code=400, detail="用户名已存在")
role = await ensure_role_assignable(db, body.role)
if is_admin(role) and not is_admin(current.role):
raise HTTPException(status_code=403, detail="只有管理员可以分配管理员角色")
email = await _ensure_email_available(db, str(body.email) if body.email else None)
if settings.email_binding_required and not is_admin(role) and not email:
raise HTTPException(status_code=400, detail="系统已开启「登录必须绑定邮箱」,请填写邮箱")
@@ -455,6 +461,7 @@ async def create_user(
email_verified=email_verified,
email_verified_at=datetime.utcnow() if email and email_verified else None,
max_accounts=normalize_max_accounts(body.max_accounts, role),
created_by=current.id,
)
db.add(user)
await db.commit()
@@ -473,6 +480,7 @@ async def update_user(
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
ensure_user_manageable(current, user)
settings = await load_settings(db)
if user.id == current.id and body.is_active is False:
raise HTTPException(status_code=400, detail="不能禁用当前登录账号")
@@ -482,6 +490,8 @@ async def update_user(
await guard_last_admin_change(db, user=user, deactivating=True)
if body.role is not None:
new_role = await ensure_role_assignable(db, body.role)
if is_admin(new_role) and not is_admin(current.role):
raise HTTPException(status_code=403, detail="只有管理员可以分配管理员角色")
await guard_last_admin_change(db, user=user, new_role=new_role)
if body.display_name is not None:
@@ -541,6 +551,7 @@ async def delete_user(
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
ensure_user_manageable(current, user)
await guard_last_admin_change(db, user=user, deleting=True)
await db.delete(user)
await db.commit()
@@ -550,7 +561,7 @@ async def delete_user(
@roles_router.get("", response_model=RolesResponse)
async def admin_list_roles(
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
_: User = Depends(require_role_manager),
):
records = await list_role_records(db)
roles = []
@@ -570,7 +581,7 @@ async def admin_list_roles(
@roles_router.get("/catalog", response_model=PermissionCatalogResponse)
async def get_permission_catalog(_: User = Depends(require_user_manager)):
async def get_permission_catalog(_: User = Depends(require_role_manager)):
return PermissionCatalogResponse(**permission_catalog())
@@ -578,7 +589,7 @@ async def get_permission_catalog(_: User = Depends(require_user_manager)):
async def create_custom_role(
body: RoleCreate,
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
_: User = Depends(require_role_manager),
):
record = await create_role(
db,
@@ -603,7 +614,7 @@ async def update_custom_role(
code: str,
body: RoleUpdate,
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
_: User = Depends(require_role_manager),
):
record = await update_role(
db,
@@ -627,7 +638,7 @@ async def update_custom_role(
async def delete_custom_role(
code: str,
db: AsyncSession = Depends(get_db),
_: User = Depends(require_user_manager),
_: User = Depends(require_role_manager),
):
await delete_role(db, code)
return {"message": "角色已删除"}