security: block non-admin roles from AdminLoginView

AdminLoginView previously accepted any valid credential regardless of
role. partner_manager / partner / partner_staff / partner_customer /
customer accounts could obtain admin JWTs and land on admin.eventifyplus.com,
where protected pages would render generic "not found" empty states.

Now returns 403 for those roles unless the user is a superuser or has an
attached StaffProfile. Writes an auth.admin_login_failed audit row with
reason=non_admin_role.

Closes gap reported for novakopro@gmail.com on /partners/3.
This commit is contained in:
2026-04-21 18:35:16 +05:30
parent 4a9f754fda
commit 64ff08b2b2
2 changed files with 15 additions and 0 deletions

View File

@@ -33,6 +33,14 @@ class AdminLoginView(APIView):
_audit_log(request, 'auth.admin_login_failed', 'auth', str(user.id),
{'identifier': identifier, 'reason': 'account_disabled'}, user=user)
return Response({'error': 'Account is disabled'}, status=status.HTTP_403_FORBIDDEN)
# Block non-admin roles from the admin dashboard (partner/customer roles must use their own portals)
NON_ADMIN_ROLES = {'customer', 'partner', 'partner_manager', 'partner_staff', 'partner_customer'}
has_staff_profile = hasattr(user, 'staff_profile') and user.staff_profile is not None
if user.role in NON_ADMIN_ROLES and not user.is_superuser and not has_staff_profile:
_audit_log(request, 'auth.admin_login_failed', 'auth', str(user.id),
{'identifier': identifier, 'reason': 'non_admin_role', 'role': user.role}, user=user)
return Response({'error': 'This account is not authorized for the admin dashboard.'},
status=status.HTTP_403_FORBIDDEN)
refresh = RefreshToken.for_user(user)
user_data = UserSerializer(user).data
# RBAC: prefer StaffProfile for allowed_modules and scopes