Why Your Moodle Role Change Broke Access Three Courses Away: Understanding Context-Scoped Permissions
Moodle permissions live in a context hierarchy, so the same role can be safe or dangerous depending on where you assign it. Learn how contexts, overrides, and Prohibit interact, and how to verify effective permissions.
07 Mar 2026, 16:07 UTC

You assign a teacher the "Manager" role at the system level so they can tweak one site setting, and a week later you discover they can now edit every course on the platform. Or the reverse: you revoke a capability at the course level and suddenly an activity that relied on it stops working. Both failures come from the same source — Moodle permissions are not flat. They live in a context hierarchy, and changes at one level propagate downward unless you know where you're standing.
The useful takeaway: before touching any role or capability, identify the context you're changing it in, because that context defines the blast radius. This post explains the hierarchy, shows how to inspect effective permissions, and gives a concrete example of scoping a role correctly.
Contexts are the unit of scope
Every permission check in Moodle happens inside a context — a node in a tree. The main levels are:
- System — the whole site. The root of the tree.
- Course category — a grouping of courses.
- Course — a single course.
- Activity/module — a forum, quiz, or assignment inside a course.
- Block and user — less commonly used, but part of the same tree.
A role assigned at a higher context applies to everything below it. Assign someone "Teacher" at the course-category level and they are a teacher in every course in that category, including ones created later. Assign them at the course level and the role stops there. This is the decision most permission bugs come down to: the right role in the wrong context.
How effective permissions are computed
When Moodle checks whether a user can, say, mod/forum:replypost, it walks the context tree from the activity up to the system level and combines:
- Role assignments — which roles the user holds in each relevant context.
- Capability definitions — each role maps capabilities to Allow, Prevent, Prohibit, or Not set.
- Overrides — explicit capability changes made in a specific context for a specific role.
The precedence rule that trips people up: Prohibit wins over everything, and it cannot be overridden lower in the tree. If you set a capability to Prohibit at the system level, no course-level Allow will restore it. Prevent is softer — a lower-level Allow can still take effect. When you genuinely want to lock something down site-wide, Prohibit is the tool; when you want a default that courses can adjust, use Prevent or leave it Not set.
A worked example: a read-only auditor
Say you need an external auditor to view course content and grades in one category, but change nothing. The wrong approach is editing the Teacher role — that affects every teacher everywhere. The right approach:
- Go to Site administration > Users > Permissions > Define roles and create a new role, e.g. "Auditor", based on the archetype closest to read-only (often starting from no archetype and adding only view capabilities).
- Allow only the capabilities you actually need, such as
moodle/course:viewandgradereport/grader:view. Leave everything else Not set. - Navigate to the target course category and use Assign roles to give the auditor account this role in that category context only.
Because the assignment is at category level, it covers current and future courses in that category — and nothing outside it. If the auditor only needs one course, assign at the course context instead. The role definition is reusable; the assignment is what scopes it.
Verifying what a user can actually do
Never trust the role definition screen alone — effective permissions are what matter. Two practical checks:
- Permissions report: Site administration > Users > Permissions > Permissions report. Pick a user and a capability, and Moodle shows the computed result across contexts. This is the fastest way to answer "why can this person do X?"
- Database inspection: on a staging copy, query
role_assignmentsfiltered bycontextidto list who holds what role in a given scope, and joincontextto see which course or category that ID represents. This requires read access to the Moodle database and is best run read-only.
Always test role changes on a staging copy of the site first. A capability change at system or category level propagates to every descendant context, and there is no single "undo" button for a mistaken Prohibit that cascaded into hundreds of courses.
The trade-off: inheritance vs. predictability
The hierarchy is powerful precisely because it propagates — but propagation is also the risk. Deep trees of overrides become hard to reason about: an Allow here, a Prevent there, and suddenly only the Permissions report can tell you the truth. A few habits keep it manageable:
- Prefer assigning existing archetype roles at the lowest context that does the job, rather than creating custom roles.
- Use overrides sparingly and document each one; an override with no recorded reason will be feared forever.
- Reserve Prohibit for genuine site-wide bans.
- If you develop plugins, namespace your capability names (
mod/yourplugin:capability) to avoid clashing with core constants.
The next time you need to grant or restrict access in Moodle, start with the question "which context?" rather than "which role?". Pick the narrowest context that covers the need, verify with the Permissions report as a test user, and you'll avoid the classic three-courses-away surprise.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.