完善权限体系

新增授权接口
This commit is contained in:
晓丰 2025-04-15 16:01:11 +08:00
parent b6a0abd9ee
commit ae6db6ec44
4 changed files with 104 additions and 3 deletions

74
accounts/api/authorize.py Normal file
View File

@ -0,0 +1,74 @@
from ninja import Router, Schema, Query
from pydantic import Field
from typing import List
from django.shortcuts import get_object_or_404
from accounts.models import User
from websites.models import Website
from utils.auth import jwt_auth
from utils.permissions import manager_required
router = Router(tags=["授权管理"])
class AuthorizeIn(Schema):
user_id: int = Field(..., description="被授权的用户ID")
website_ids: List[int] = Field(..., description="要授权的网站ID列表")
@router.post("/authorize", auth=jwt_auth)
@manager_required
def authorize_user(request, data: AuthorizeIn):
manager = request.user
target_user = get_object_or_404(User, id=data.user_id)
if target_user.role != "user":
return {"success": False, "message": "只能授权给普通用户"}
managed_ids = set(manager.managed_websites.values_list("id", flat=True))
for wid in data.website_ids:
if wid not in managed_ids:
return {"success": False, "message": f"无权授权网站ID{wid}"}
target_user.authorized_websites.add(*data.website_ids)
return {
"success": True,
"message": f"已授权 {target_user.username} 访问 {len(data.website_ids)} 个网站",
}
@router.get("/authorized-sites", auth=jwt_auth)
@manager_required
def get_user_authorized_sites(request, user_id: int = Query(...)):
target_user = get_object_or_404(User, id=user_id)
if target_user.role != "user":
return {"success": False, "message": "只能查看普通用户的授权信息"}
sites = target_user.authorized_websites.all().values("id", "name", "db_alias")
return {
"success": True,
"user": target_user.username,
"authorized_websites": list(sites)
}
@router.post("/revoke", auth=jwt_auth)
@manager_required
def revoke_authorization(request, data: AuthorizeIn):
manager = request.user
target_user = get_object_or_404(User, id=data.user_id)
if target_user.role != "user":
return {"success": False, "message": "只能撤销普通用户的授权"}
managed_ids = set(manager.managed_websites.values_list("id", flat=True))
for wid in data.website_ids:
if wid not in managed_ids:
return {"success": False, "message": f"无权撤销网站ID{wid}"}
target_user.authorized_websites.remove(*data.website_ids)
return {
"success": True,
"message": f"已撤销 {target_user.username}{len(data.website_ids)} 个授权网站"
}

View File

@ -1,6 +1,6 @@
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db import models from django.db import models
from websites.models import Website
class User(AbstractUser): class User(AbstractUser):
ROLE_CHOICES = [ ROLE_CHOICES = [
@ -9,7 +9,18 @@ class User(AbstractUser):
('user', '普通用户'), ('user', '普通用户'),
] ]
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user', help_text="用户角色") role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user', help_text="用户角色")
managed_websites = models.ManyToManyField(
Website,
blank=True,
related_name="managers",
help_text="分管理可管理的网站"
)
authorized_websites = models.ManyToManyField(
Website,
blank=True,
related_name="authorized_users",
help_text="普通用户被授权可访问的网站"
)
def is_admin(self): def is_admin(self):
return self.role == 'admin' return self.role == 'admin'

2
api.py
View File

@ -2,8 +2,10 @@ from ninja import NinjaAPI
from resumes.api.views import router as resume_router from resumes.api.views import router as resume_router
from accounts.api.auth import auth_router from accounts.api.auth import auth_router
from accounts.api.user import user_router from accounts.api.user import user_router
from accounts.api.authorize import router
api = NinjaAPI(title="简历管理 API") api = NinjaAPI(title="简历管理 API")
api.add_router("/resumes/", resume_router) api.add_router("/resumes/", resume_router)
api.add_router("/auth", auth_router) api.add_router("/auth", auth_router)
api.add_router("/users", user_router) api.add_router("/users", user_router)
api.add_router("/authorize", router)

View File

@ -1,11 +1,16 @@
from ninja import Router, Query from ninja import Router, Query
from accounts.models import User
from resumes.models import ResumeBasic from resumes.models import ResumeBasic
from resumes.api.schemas import ResumeBasicOut, PaginatedResumes from resumes.api.schemas import ResumeBasicOut, PaginatedResumes
from typing import Optional from typing import Optional
from utils.auth import jwt_auth
from utils.permissions import login_required
router = Router(tags=["简历"]) router = Router(tags=["简历"])
@router.get("/", response=PaginatedResumes) @router.get("/", response=PaginatedResumes, auth=jwt_auth)
@login_required
def list_resumes( def list_resumes(
request, request,
job_status: Optional[str] = Query(None), job_status: Optional[str] = Query(None),
@ -16,8 +21,16 @@ def list_resumes(
limit: int = 10, limit: int = 10,
offset: int = 0 offset: int = 0
): ):
user = request.user
qs = ResumeBasic.objects.all() qs = ResumeBasic.objects.all()
if user.is_admin():
pass # 管理员访问全部
elif user.is_manager():
qs = qs.filter(source_id__in=user.managed_websites.values_list("id", flat=True))
elif user.is_user():
qs = qs.filter(source_id__in=user.authorized_websites.values_list("id", flat=True))
if job_status: if job_status:
qs = qs.filter(job_status=job_status) qs = qs.filter(job_status=job_status)
if age: if age:
@ -28,6 +41,7 @@ def list_resumes(
qs = qs.filter(source_id=source_id) qs = qs.filter(source_id=source_id)
if keyword: if keyword:
qs = qs.filter(crawl_keywords__icontains=keyword) qs = qs.filter(crawl_keywords__icontains=keyword)
total = qs.count() total = qs.count()
results = qs[offset:offset + limit] results = qs[offset:offset + limit]