Troubleshooting Access

Use this page when a user can see a screen, menu, action or report they should not see, or cannot see something they should. Diagnose the source before changing roles or privileges.

First command

powershell
python manage.py rbac_explain --user <email> --target <app>

The report shows:

  • is_superuser, is_staff and active status;
  • active and inactive roles;
  • every linked privilege;
  • fallback and legacy links;
  • role overrides;
  • effective models, menus, actions and outputs with the privilege that granted each one.

Diagnostic order

Use this order so a stale catalogue or a broken template is not mistaken for a role problem:

  1. python manage.py check
  2. python manage.py rbac_sync
  3. python manage.py rbac_bootstrap
  4. python manage.py rbac_audit --strict --list --list-role-risks
  5. python manage.py rbac_explain --user <email> --target <app>
  6. Reload the page in a fresh request (the permission snapshot is request-scoped).

If rbac_sync fails, fix that first. The role data is not updated until the sync transaction commits.

Cause matrix

Symptom Check Fix
User sees everything is_superuser=True This is intended for unassigned elements. Do not use superuser as a normal staff account.
User sees an app they were not granted User has a second role, or NORMAL_USER has the app's privilege Remove the extra role or privilege; use a task privilege. rbac_sync removes .admin and legacy system.administrator links from NORMAL_USER.
User sees a newly added screen Fallback privilege linked to a role, a non-baseline privilege linked to NORMAL_USER, or a task target still in a broad .use bundle Run rbac_sync; it deactivates task targets in .use bundles. rbac_audit --list-role-risks lists remaining risks.
User sees a screen after a sync The target was added to an existing privilege Check the privilege's element list; normal syncs are not supposed to widen an existing privilege.
User sees HR through /admin/ is_staff=True plus Django model permissions Unregister the business model or use RbacAdminModelAdminMixin.
User can open an HR report or dashboard without an HR role function.reports.use still contains an HR output from an older sync Run rbac_sync; it deactivates task targets inside broad .use bundles. Check rbac_audit --list-role-risks.
Menu is granted but access fails The menu gate requires a model/action/output permission Grant the gate operation, or attach the menu to the task that includes it.
A granted module menu causes an error Self-gating recursion Run rbac_sync after the self-gate fix; confirm gate_target_code != code.
User can open a screen but not a related lookup The related model lacks view Add view for that related model to the task.
Report is hidden Module menu or output target not granted Add the module menu and the output code to the task.
Access changes are not taking effect Request-scoped snapshot is cached on the request Reload the page; a new request rebuilds the snapshot.
NORMAL_USER gets 403 on /dashboards/enezacommon/ The Common module menu is not granted, or the module menu target does not exist Run rbac_sync; confirm EnezacommonConfig.app_entry_menu is set and menu.enezacommon.__module__ exists.
A 403 response is plain text or Django's bare page Django cannot find 403.html, or a custom handler bypasses it Confirm enezacommon is installed, APP_DIRS=True, and enezacommon/templates/403.html exists. Restart the server after adding it in production.
The 403 page loads without its designed styling A project-level 403.html is shadowing the shipped template, or the server cached the old template Remove the shadowing template or restart/clear the template cache. The shipped page uses inline CSS and needs no static files.
Dashboard is visible but a tab or widget returns 403 The dashboard access_code or a widget output is missing Grant the missing output or dashboard access code; check rbac_explain. For the seeded HR roles, run python manage.py seed_hr_roles.
HR manager dashboard shows report_forbidden on every widget The HR_MANAGER role has the HR menus and models but no HR report outputs Run python manage.py seed_hr_roles; it adds hr.reports.manager.view. A custom role can instead receive task.humanresource.reports.view.

Common dashboard: /dashboards/enezacommon/

NORMAL_USER should reach the Common module through the baseline function.common.use privilege. Check these in order:

  1. EnezacommonConfig has menu_verbose_name = "Common" and app_entry_menu = "enezacommon:defaultDashboard".
  2. python manage.py rbac_sync reports the Common menu target as protected.
  3. python manage.py rbac_explain --user <email> --target enezacommon shows menu.enezacommon.__module__ with function.common.use as its source.
  4. The my_workspace dashboard is active, published and has a published version. An unpublished dashboard is hidden even when the menu is granted.
  5. The dashboard has no access_code that the user lacks.
  6. If the page renders but widgets show permission errors, the widget report outputs are missing. function.reports.use grants normal report outputs; task-scoped outputs must be granted by their task. The seeded HR_MANAGER and HR_ASSISTANT roles receive their reports through hr.reports.manager.view and hr.reports.assistant.view; rerun python manage.py seed_hr_roles after report changes.

If rbac_sync was run before app_entry_menu was added, the module target does not exist yet. The sync is safe to rerun; it creates the target, the function.common.use privilege and the NORMAL_USER link.

403 page troubleshooting

The project ships enezacommon/templates/403.html. Django's default 403 handler looks for a template named 403.html in the app template directories.

  • Confirm APP_DIRS=True is configured.
  • Confirm enezacommon is in INSTALLED_APPS.
  • Confirm no project-level 403.html or custom handler403 shadows the template.
  • The shipped page uses inline CSS and no static files. If it looks unstyled, check for a shadowing template or a cached old template, then restart the server.
  • The theme mirrors the Alpine _x_theme value: dark renders dark, while light and system render light (the current app layout does not resolve system to dark). No stored value falls back to the app default, dark.
  • A signed-out user is redirected to login rather than shown 403. The 403 page is for an authenticated account that lacks the required task.
  • After adding or changing the template, restart the server or clear the template cache in production.

The page intentionally shows only the signed-in user and the 403 reference. It does not expose the required privilege code or hidden route names.

rbac_sync failures

Error Likely cause Fix
NameError or ImportError after an upgrade The running shell or server has stale code Restart it and run python manage.py check.
OperationalError: no such table Migrations have not run in this environment Run python manage.py migrate.
ValidationError about a privilege code A custom privilege uses an invalid task. code or a non-system function. code Use task.<app>.<object>.<operation> or rename the custom code.
ImproperlyConfigured about app_labels A user_facing=True spec uses app_labels without allow_app_wide_use=True Replace the app-wide selector with explicit labels/menus/actions.
Sync completes but the old access remains The source is a custom privilege or a role override, not the fallback Run rbac_explain and remove the custom link or override.

rbac_sync is atomic. If it fails, none of its changes commit; fix the error and rerun it. Warnings W003 and W004 seen before the sync are expected when an older database still has fallback or admin links. They should disappear after a successful sync.

Read-only checks

Run these in python manage.py shell only after taking a backup and only when the user asks for diagnosis.

python
from django.contrib.auth import get_user_model
from enezarbac.models import RolePrivilege, UserRole

user = get_user_model().objects.get(email="user@example.com")
print(user.is_superuser, user.is_active)
print(list(UserRole.objects.filter(user=user).values_list("role__code", flat=True)))
print(list(
    RolePrivilege.objects.filter(role__user_roles__user=user)
    .values_list("role__code", "privilege__code")
))

Check the fallback and legacy links:

python
from enezarbac.models import RolePrivilege
print(list(
    RolePrivilege.objects.filter(
        privilege__code__in=[
            "function.unclassified.admin",
            "system.administrator",
        ]
    ).values_list("role__code", "privilege__code")
))

Check whether HR is still unclassified:

powershell
python manage.py rbac_audit --list-unclassified --list-role-risks

What not to do

  • Do not add the user to SYSTEM_ADMINISTRATOR to "fix" a missing screen.
  • Do not link function.unclassified.admin to a role.
  • Do not mark the endpoint @rbac_public.
  • Do not disable RBAC_SUPERUSER_BYPASS to solve a role problem.
  • Do not run rbac_bootstrap --reset-functional to hide a manual grant; reset restores seeded definitions.

When the audit is clean

Enable strict denial only after the audit is clean:

python
RBAC_DENY_UNREGISTERED_ENDPOINTS = True

Then run:

powershell
python manage.py check
python manage.py rbac_audit --strict

The user is solely responsible for testing and validation.