From 64ff08b2b2adcad6751523888d003a4c9010b1c6 Mon Sep 17 00:00:00 2001 From: Sicherhaven Date: Tue, 21 Apr 2026 18:35:16 +0530 Subject: [PATCH] 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. --- CHANGELOG.md | 7 +++++++ admin_api/views.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b758fe..b03f06a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), version --- +## [1.14.1] — 2026-04-21 + +### Security +- **`AdminLoginView` now rejects non-admin roles** — users with `role` in `{customer, partner, partner_manager, partner_staff, partner_customer}` can no longer obtain an admin JWT via `POST /api/v1/auth/login/`. Returns HTTP 403 with `{'error': 'This account is not authorized for the admin dashboard.'}` and writes an `auth.admin_login_failed` audit row with `reason: 'non_admin_role'`. Superusers and any user with an attached `StaffProfile` remain allowed regardless of role, so existing admin staff are unaffected. Closes the gap where partner_manager accounts (e.g. `novakopro@gmail.com`) could log into `admin.eventifyplus.com` and hit protected routes + +--- + ## [1.14.0] — 2026-04-21 ### Added diff --git a/admin_api/views.py b/admin_api/views.py index 055d860..7ecc20a 100644 --- a/admin_api/views.py +++ b/admin_api/views.py @@ -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