feat: add Teacher Admin role to four-tier permission system
Introduces a four-tier role system: Regular User → Student Admin → Teacher Admin → Super Admin. Teacher Admin can manage own contests, problemsets, and view classroom data. Student Admin (renamed from Admin) retains problem management only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,12 @@ class super_admin_required(BasePermissionDecorator):
|
||||
return user.is_authenticated and user.is_super_admin()
|
||||
|
||||
|
||||
class teacher_admin_required(BasePermissionDecorator):
|
||||
def check_permission(self, request):
|
||||
user = request.user
|
||||
return user.is_authenticated and user.is_teacher_or_above()
|
||||
|
||||
|
||||
class admin_role_required(BasePermissionDecorator):
|
||||
def check_permission(self, request):
|
||||
user = request.user
|
||||
|
||||
18
account/migrations/0006_alter_user_admin_type.py
Normal file
18
account/migrations/0006_alter_user_admin_type.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.4 on 2026-06-03 00:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('account', '0005_alter_user_is_disabled_alter_user_open_api_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='admin_type',
|
||||
field=models.TextField(choices=[('Regular User', 'Regular User'), ('Student Admin', 'Student Admin'), ('Teacher Admin', 'Teacher Admin'), ('Super Admin', 'Super Admin')], default='Regular User'),
|
||||
),
|
||||
]
|
||||
27
account/migrations/0007_rename_admin_to_student_admin.py
Normal file
27
account/migrations/0007_rename_admin_to_student_admin.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 6.0.4 on 2026-06-03 00:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def rename_admin_to_student_admin(apps, schema_editor):
|
||||
User = apps.get_model("account", "User")
|
||||
User.objects.filter(admin_type="Admin").update(admin_type="Student Admin")
|
||||
|
||||
|
||||
def rename_student_admin_to_admin(apps, schema_editor):
|
||||
User = apps.get_model("account", "User")
|
||||
User.objects.filter(admin_type="Student Admin").update(admin_type="Admin")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("account", "0006_alter_user_admin_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
rename_admin_to_student_admin,
|
||||
rename_student_admin_to_admin,
|
||||
),
|
||||
]
|
||||
@@ -7,7 +7,8 @@ from utils.models import JSONField
|
||||
|
||||
class AdminType(models.TextChoices):
|
||||
REGULAR_USER = "Regular User", "Regular User"
|
||||
ADMIN = "Admin", "Admin"
|
||||
STUDENT_ADMIN = "Student Admin", "Student Admin"
|
||||
TEACHER_ADMIN = "Teacher Admin", "Teacher Admin"
|
||||
SUPER_ADMIN = "Super Admin", "Super Admin"
|
||||
|
||||
|
||||
@@ -56,14 +57,24 @@ class User(AbstractBaseUser):
|
||||
def is_regular_user(self):
|
||||
return self.admin_type == AdminType.REGULAR_USER
|
||||
|
||||
def is_admin(self):
|
||||
return self.admin_type == AdminType.ADMIN
|
||||
def is_student_admin(self):
|
||||
return self.admin_type == AdminType.STUDENT_ADMIN
|
||||
|
||||
def is_teacher_admin(self):
|
||||
return self.admin_type == AdminType.TEACHER_ADMIN
|
||||
|
||||
def is_super_admin(self):
|
||||
return self.admin_type == AdminType.SUPER_ADMIN
|
||||
|
||||
def is_admin_role(self):
|
||||
return self.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN]
|
||||
return self.admin_type in [
|
||||
AdminType.STUDENT_ADMIN,
|
||||
AdminType.TEACHER_ADMIN,
|
||||
AdminType.SUPER_ADMIN,
|
||||
]
|
||||
|
||||
def is_teacher_or_above(self):
|
||||
return self.admin_type in [AdminType.TEACHER_ADMIN, AdminType.SUPER_ADMIN]
|
||||
|
||||
def can_mgmt_all_problem(self):
|
||||
return self.problem_permission == ProblemPermission.ALL
|
||||
|
||||
@@ -105,7 +105,9 @@ class UserAdminAPI(APIView):
|
||||
user.admin_type = data["admin_type"]
|
||||
user.is_disabled = data["is_disabled"]
|
||||
|
||||
if data["admin_type"] == AdminType.ADMIN:
|
||||
if data["admin_type"] == AdminType.STUDENT_ADMIN:
|
||||
user.problem_permission = data["problem_permission"] or ProblemPermission.OWN
|
||||
elif data["admin_type"] == AdminType.TEACHER_ADMIN:
|
||||
user.problem_permission = data["problem_permission"] or ProblemPermission.OWN
|
||||
elif data["admin_type"] == AdminType.SUPER_ADMIN:
|
||||
user.problem_permission = ProblemPermission.ALL
|
||||
|
||||
@@ -432,7 +432,7 @@ class UserRankAPI(AsyncAPIView):
|
||||
n = 0
|
||||
|
||||
profiles = UserProfile.objects.filter(
|
||||
user__admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
||||
user__admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||
user__is_disabled=False,
|
||||
user__username__icontains=username,
|
||||
).select_related("user").filter(accepted_number__gte=0).order_by("-accepted_number", "submission_number")
|
||||
|
||||
Reference in New Issue
Block a user