Repository navigation
Sync 3.5 to 3.6 - #2579
Sync 3.5 to 3.6#2579
Conversation
At login the session holds every stored grant of the role, defined by a permissions row or not, so a plugin's own key authorizes as much as a core one. adb4e31 compared only grants of defined permissions on both sides: a role carrying such a key could be saved, assigned or deleted by a role manager who does not hold it. Both sides now follow the runtime grants. RoleGuard::heldBy() asks the actor's hasPermission() for every defined key and every key any role is granted; RoleGuard::reachOf() takes the target's stored grants without the obsolete keys the earlier default seeders left (RoleGuard::OBSOLETE: role_actionok, action_ok, which nothing checks) and without the always-on permissions, which a role save stores on every role whoever saves it. Role save, delete and assignment use the same two helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto 3.6.x Both sides of the role reach check follow the grants that authorize at runtime, custom keys included; only the obsolete role_actionok/action_ok and the always-on permissions are left out (review of evolution-cms#2578). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I would hold the merge at 39f789d until the remaining role-authorization concern is resolved. This sync includes the same disabled/always-on permission exception discussed in the latest #2578 review. Please keep the reach comparison consistent with effective runtime grants for each operation, and cover varied delegated save/assign/delete permissions and existing roles with controller-level tests. There is also a separate consistency issue in the assignment controller: the authorization check uses an integer-cast role ID, while the original input is passed on for persistence. That could make the checked and stored role differ depending on input handling and database coercion. Please strictly validate the role ID once and use that same canonical, checked value throughout authorization and persistence. Add a regression test proving that the role written is exactly the role whose permissions were checked, including rejection of invalid or ambiguous input. These are static review observations. I have not reproduced either case at runtime, and the database-coercion consequences of the role-ID mismatch remain unverified. SokilAI |
… rule 044ecd3 left always-on permissions out of a target role's reach, because a role save stores them on every role. Only a manager holding save_role can save, though: one allowed to delete a role, or holding a narrower set, could act on a role whose always-on grants it lacks, and those grants authorize at runtime like any other. reachOf() now leaves out only the obsolete keys. A non-administrator stores on a saved role only grants it holds, always-on ones included. The decision EditOrNewUser makes before it changes a user's role, save_role plus the reach check, moves to RoleGuard::mayAssignRole(). The tests run the real role save and delete and mayAssignRole() for save_role / delete_role combinations, fresh and upgraded installs, custom permissions with and without a permissions row, and an actor whose role lacks an always-on grant the target holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3.6.x Always-on grants count in role reach, a role save stores only grants the actor holds, and assignment goes through RoleGuard::mayAssignRole() (second review of evolution-cms#2578). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EditOrNewUser checked the reach of (int) $_POST['role'] but passed the
posted string to setRole(). MySQL and MariaDB in the sql_mode the
installer configures coerce it on write ('0.5' becomes 1), so the stored
role could be one the check never saw. RoleGuard::postedRoleId() now
accepts only "0" or the decimal id of an existing role, validated once
before anything is saved, and that same int goes to the TV lookup, the
reach check and setRole().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, both points are fixed on #2578 and merged here.
Full 3.6 suite in |
Follow-up to #2577, which was merged before the review of #2578 was addressed.
Brings in #2578 (against 3.5.x, still open). Role reach now compares the grants that authorize at runtime, on both sides:
RoleGuard::heldBy(): the actor's ownhasPermission()for every defined key and every key any role is granted, custom ones included;RoleGuard::reachOf(): the target's stored grants, always-on ones included, less only the obsoleterole_actionok/action_okthe earlier seeders left;RoleGuard::mayAssignRole()(save_roleplus reach);RoleGuard::postedRoleId():"0"or the decimal id of an existing role) before anything is saved, and that same int is checked and stored. Before, the posted string reachedsetRole(), and MySQL/MariaDB in the installer's non-strict sql_mode could coerce it into a role that was never checked.This replaces the earlier version merged with #2577, which compared only defined permissions. That let a role carrying a plugin key with no
permissionsrow be saved, assigned or deleted by a manager who does not hold the key.Role save, delete and assignment use the same helpers. 3.6 has no other role reach check.
Verification: merges into 3.6.x without conflicts. Full suite in
evo-ci-php:8.4: 2739 passed, 12 skipped;UserRoleAssignmentTestalso on MySQL 8.4. PHPStan: 0 errors.🤖 Generated with Claude Code