OJS version: 3.5.0-4
Server: Hostinger shared hosting (LiteSpeed), PHP 8.3, MySQL/MariaDB
Problem:
As the Site Administrator (user_id 1, also holds Journal Manager role in the
only journal), two admin pages fail:
-
/management/settings/access (Users & Roles)
→ “You are not authorized to access the requested resource.” -
/management/settings/user/1 (Edit My Profile)
→ HTTP 500 (blank page)
Browser console shows the underlying cause on both pages:
Failed to load resource: 401 (userRoleAssignment)
So the page loads and then its background API call to userRoleAssignment
returns 401.
What I have already checked and ruled out:
- Account is NOT disabled (users.disabled = 0)
- Account holds role_id 1 (Site Admin) at context NULL, and role_id 16
(Manager) in the journal context — verified in user_user_groups - config: installed = On
- Cleared cache/t_compile, cache/_db, cache/*.php — no change
- Fresh login / incognito / full cookie clear — no change
- Found and removed TWO duplicate rows in user_user_groups for user_id 1
(user_group_id 14 and 16 each appeared twice, one with NULL date_start
and one with a timestamp). Removing duplicates did NOT fix the pages. - No orphaned role rows: every user_user_groups row for user 1 points to a
valid existing user_group. - The failing request runs CLEAN via PHP CLI (bootstrapping index.php with
the same REQUEST_URI produces no error) — it only 500s / 401s through the
web server.
Because it works via CLI but fails via web, and the failing call is an API
endpoint returning 401, I suspect either an OJS authorization policy issue on
the userRoleAssignment handler, or the web server (LiteSpeed/mod_security)
stripping an auth/CSRF header on AJAX/API requests.
Questions:
- What authorization policy governs the userRoleAssignment API endpoint, and
what would make it return 401 for a valid site admin? - Could a stripped Authorization/CSRF header cause this, and how can I confirm
it server-side? - Any known issues with duplicate/expired role rows affecting this endpoint
in 3.5.0-4?
Context: this journal recently went through a migration and a security cleanup,
so I’m open to it being residual data or a server-config issue.