Note: You are currently viewing documentation for Moodle 1.9. Up-to-date documentation for the latest stable version is available here: How permissions are calculated.

How permissions are calculated: Difference between revisions

From MoodleDocs
 
(28 intermediate revisions by 7 users not shown)
Line 1: Line 1:
{{Roles}}
{{Roles}}
One of the most frequently asked question is: ''What permissions does a user have in a given context?'' This article will document the main function in Moodle that is used to answer the question.  The function is ''has_capability()''. It can be found in ''lib/accesslib.php''. The function implements the rules for "summing" permissions from multiple roles and overrides. Given a user, a capability, and a context, the function returns ''true'' if the user is allowed to perform the action controlled by the capability in the given context, and ''false'' otherwise. 


This article is written for non-programmers as well as programmers. It describes ''what'' the function does, not ''how''. The ''how'' is complicated. The ''what'' turns out to be surprisingly simple!
A frequently asked question is, "What are my permissions in this context?". This article attempts to explain how Moodle answers that question. The article is written for non-programmers as well as programmers. It describes what the function does, not how it does it. If you are a programmer and want to see the how, look at the functions has_capability_in_accessdata and has_capability() in lib/accesslib.php.  


Notice that the function ''does not'' attempt to answer the Big Question posed in the first sentence of this article. Instead, it answers the little question "Can some user do ''CAP'' here?" where ''CAP'' is a specific capability. Moodle never calculates a user's complete set of permissions.  It would be very costly to do so, and also wasteful, since most of the permissions would never be tested.  Instead, Moodle calculates permissions lazily, i.e., on demand. Moodle does not cache calculated permissions, but recalculates them every time they need to be tested. This is why role assignments and overrides no longer have delayed effects, as they did in Moodle 1.7 and 1.8.
Incidentally, you will be able generate permission tables like the ones in this article using the 'Explain' links on the new [[Development:Roles_administration_improvements_for_Moodle_2.0#Check_permissions_page|Check permissions]] page in Moodle 2.0. In earlier version, you can install the [[The rolesdebug.php roles debugging script|rolesdebug.php roles debugging script]].


You can generate tables like the ones in this article using [[The rolesdebug.php roles debugging script]].  This can be very helpful in debugging roles-related problems.


==The calculation==
==Clarifying the question==
 
Let us refine the question "What are my permissions in this context?"
 
Naturally, '''my''' can be any user that Moodle knows about. If you don't specify a particular user, it will default to the currently logged in user.
 
'''Permissions''' can be broken down into a computed permissions of '''true''' or '''false''' for each '''capability'''. The different capabilities are completely independant (apart from the magic 'moodle/site:doanything' capability which will be explained later).
 
A '''context''' is a part of a Moodle site. For example a course, a course category, an activity, or so on. Contexts are nested, so an activity will be in a course, which will be in one or more categories, which will be in the whole system. The most specific context that you are in is the most important, and is the one that should be used when asking the question. The other, less-specific contexts that contain this one also influence the result.


The function is called as follows:
So, while our original question was "What are my permissions in this context?" the question Moodle will answer is "Does user ''$user'' have capability ''$capability'' in context ''$context''?" and the answer will be 'Yes' or 'No'.


    has_capability(CAP,CONTEXT,USER);


where
==The calculation==
*CAP is the capability being tested (e.g., mod/quiz:attempt)
*CONTEXT can be System context or some context nested arbitrarily deeply within System. Internally, the function uses Unix-style paths to represent contexts (e.g., /1/3/4/150), but such details do not concern us here
*USER is the user.


The function returns a true/false result
As explained in the previous section, we want to know whether has_capability($capability, $context, $user) returns true or false.


* ''true''  means the user should be allowed to perform the action
In order to calculate the answer, Moodle needs to combine various pieces of data.
* ''false'' means the user should be prevented from perfoming the action


===Data used by the function===
===Data used by the function===


Suppose USER is about to attempt a quiz (''CAP = mod/quiz:attempt'') in a Module context nested four levels deep within System.
====Relevant contexts====
 
To simplify the explanation, let us work with a particular example. Suppose that user $user wants to reply to a forum post ($capability is 'mod/forum:replypost') in a particular forum. And suppose that the forum is in a course in Subcategory B which is in Category A in this Moodle site. That is, the relevant contexts are:


      System
      System
         |
         |
     Category A
     Category A
Line 36: Line 39:
       Course
       Course
         |
         |
       Quiz <--- the user is here
       Forum <--- the user is here


====Role assignments====


The quiz module will call ''has_capability()'' to see if the user should be allowed to perform this action.
All the roles that the user has been assigned in all the relevant contexts will influence the outcome of the has_capability question.


The function considers the following permissions data:
A natural example would be that a user has the role 'Student' assigned in context 'Course', and role 'Authenticated User' assigned in context System.


* ''Role definitions'' (these are in the System context)
However, our example is going to be unnatural and complex, in order to demonstrate all the possible complexities. In our example the user has
* ''Role assignments'' which have been made in any of the five contexts
* Role R1 in context System
* ''Role overrides'' which may occur in any of the contexts except System (there is no concept of override in System). 
* Roles R2 and R3 in context Subcategory B
* Roles R1 and R4 in context Forum


Note that there may be multiple assignments and/or overrides in any single context. 
====Role definitions====


Whether or not the user can attempt the quiz is a function of the permissions data, as well as the location of the data in the context chain.
Naturally, the definition of each role affects the outcome of has_capability. (Role definitions are edited at Administration ► Users ► Permissions ► Define roles.) The role definition sets a permissions of 'Not set', 'Allow', 'Prevent' or 'Prohibit' for each capability.


''has_capability()'' considers all roles and overrides that impact USER in CONTEXT, but it ignores capabilities other than CAP. This simplification lets us view each role or override as a ''single permission'' having one of the following values
As mentioned above, different capabilities are independent, so, for example, we only need to specify the settings for the 'mod/forum:replypost' capability.
* For role R1, 'mod/forum:replypost' is set to Allow.
* For roles R2 and R3, 'mod/forum:replypost' is Not set.
* For role R4, 'mod/forum:replypost' is set to Prevent.
Remember, this is an intentionally complex and unnatural example.


    N - Not set
====Permission overrides====
    A - Allow
    P - Prevent
    X - Prohibit


We use the following notation: for a role ''R1'' with permission ''P'', we write ''R1(P)''.
In Moodle you can override the permissions for a role in a particular context. You can think of that as changing the definition of the role in that context. An example would be "In this category of courses, we want to prevent students from posting to forums." In the absence of an override to a different permission, you can imagine that there is an override of 'Inherit' which means use the permission from higher up the chain of contexts.


===An example===
In our ongoing example, we will assume the following overrides have been set up:
 
* In context Course,
In the quiz example, suppose that the user has four roles (''R1'', ''R2'', ''R3'', and ''R4'') and each of the roles has been assigned and overridden as follows:
** for role R2, the permissions for 'mod/forum:replypost' is overridden to Prevent
 
** for role R3, the permissions for 'mod/forum:replypost' is overridden to Allow.
 
    0    System      <---- define R1(A), R2(N), R3(N), R4(P)
            |              assign R1
            |
    1  Category A    <-------------- overide R1(N) and R4(N)
            |                         
            |                         
    2  Subcategory B  <---- assign R2 and R3
            |
            |
    3    Course      <-------------- override R2(X) and R3(A)
            |
            |
    4    Quiz        <---- assign R4 and R1
 
 
Note that ''R1'' has been assigned in two different contexts. This is an unusual practice (it is probably someone's error), but since it is legal, we have to consider the possibility that it can happen.  In fact this entire example has been contrived to explore the edge cases of the algorithm.  In practice, the calculation of permissions is trivial and the function calculates the result you would expect based on common sense and simple mental analysis (for further discussion of this, see [[#A note about the algorithm|A note about the algorithm]] near the end of this article.


===Representing the data in tabular form===
===Representing the data in tabular form===


We will set up a table to hold the permission data.  The table has
All the data mentioned in the previous section is best seen by writing it in a table.
*a column for each context, ordered from highest level (System) to lowest (the context where we want to compute the permission).
*a row for each context, listed from highest to lowest.
 
[[Image:Calculation-3A.png|Empty table]]
 
The role definitions go in the first row.  Put each definition in the column(s) coresponding to the context(s) in which the role is assigned:
 
[[Image:Calculation-5A.png|Table with assignments added]]
 
The overrides go in the remaining rows.  Each override goes
*in the same column as the role being overridden
*in the row corresponding the context in which the override is made.
 
To illustrate, let's add just one of the overrides:
 
[[Image:Calculation-6A.png|Table with just one override]]
 
R2(X) represents the override to role R2 made in the course context.  Let's add the rest of the overrides:
 
[[Image:Calculation-7A.png|Table with remaining overrides shown]]
 
The table is now fully populated, and we are ready to calculate the permission.
 
===The algorithm===
 
The algorithm follows a path through the table indicated by nodes and arrows in the diagram below and stops as soon as it has a conclusive result.
 
[[Image:Algorithm_path_through_permission_table_A.png|Algorithm path through permissions data]]
 
# If there is an X anywhere in the table, STOP.  The calculated permission is ''X''
# Go to the START node
# If there is no next node, STOP.  The calculated permission is ''P''
# Otherwise, follow the arrow to the next node
#Add the permissions in the node using the numerical equivalents: N = 0, A = +1, P = -1
#* If the sum is positive, STOP. The calculated permission is ''A''
#* If the sum is negative, STOP. The calculated permission is ''P''
#* If the sum is zero, GO TO step 3
 
Notice that the algorithm stops when either (1) a conclusive result is obtained, or (2) the algorithm reaches the last node without a conclusive result.
 
===Applying the algorithm to our example===
 
If we execute the algorithm with the quiz data, we stop at step 1 with a calculated permission of X.  That was too easy, so let's change the X to a P and see what happens:
 
        R1  R2  R3  R4  R1
      +-----+---------+--------
    0 |  A  | A    P  | P    A
    1 |  N  |        | N    N
    2 |    |        |
    3 |    | P    A  |
    4 |    |        |


Run the algorithm:
In the table, there will be one row for each context, and at the start of each row, we write the name of the roles assigned in that context.


*There is No X in the table
And there  will also be one column for each context, and in that column we write any permission overrides for the roles in each row.
*Go to the START node
*Go to the next node
*N + N = 0
*the sum is zero; go to the next node
*A + P = 0
*the sum is zero; go to the next node
*P + A = 0
*the sum is zero; go to the next node
*A + P = 0
*the sum is zero; go to the next node
*N = 0
*the sum is zero; go to the next node
*A = +1
*the sum is positive; STOP. The calculated permission is ''A''


If the calculated permission is ''A'', ''has_capability()'' returns ''true'', and the user is allowed to perform the action.
Except that in the column that represents the System context, we write the permissions from the role definition.


Had the calculated permission been ''P'' or ''X'', ''has_capability()'' would not immediately return ''false''. Rather, it would test if the user has ''moodle/site:doanything = Allow'' (since this permission trumps all others). The function does this by calling itself:
Normally, we think of contexts like folders in a file system, with the System context at the top, and the Forum context at the bottom. However, for this table, it turns out to be simpler later if we reverse the normal order, and put the Forum context in the first row and column, and the System context at the end.


    final result = has_capability(moodle/site:doanything,CONTEXT, USER);
The table for the example we have been building up looks like this:


==A note about the algorithm==
[[Image:Role_calc_table1.png]]


Earlier, we said that the algorithm "calculates the result you would expect." But what do we expect?  Intuition tells us that permissions closer to the user should carry more weight than more distant permissions.  That's why we set up the table the way we did and why the algorithm walks the table in the order that it does.  The last column of the table represents the role assignment(s) closest to the user.  The second-to-last column represents the role assignent(s) that are next in terms of distance from the user, and so-on.  That's why the algorithm considers the columns from right-to-left.  Within a column, the algorithm searches upward in order to give higher weight to overrides that are closer to the user.  Once again, this matches our intuition about overrides.  If there are no overrides in a column (or the overrides are all Not set), the permission in the role definition is used.
===The calculation===


==Getting the result you expect==
The calculation proceeds by working through the table in normal reading order, left to right, top to bottom. (That is why we put the Forum context first and the System context last.)


The calculation is more likely to give "the result you would expect" if you keep your permissions as simple as possible. Therefore, we recommend the following 'rules' for defining and overriding roles. If you understand the description of the calculation above, you should understand why they make sense.
# First, look through the entire table and see if there are any Prohibit permissions anywhere. If there are, stop. The final answer is false - the user does not have this capability.
# If there are no Prohibits, start at the beginning of the first row with role assignments, and go along until you get to a cell containing 'Allow' and/or 'Prevent'. In this cell,
#* If there are more Allows than Prevents, stop. The answer is true - the user has this capability.
#* If there are more Prevents than Allows, stop. The answer is false - the user does not have this capability.
#* If the number of Prevents and Allows is equal, jump down to the start of the next row with role assignments, and repeat this step 2.
# If you get to the end of the table without passing a cell with different numbers of Prevents and Allows, then the answer is false - the user does not have this capability.


===When defining roles===
====Calculating the answer to our example====


... use Allow (A) for things you want the role to be able to do, and use Not set (N) for things you don't care about (which should include most things).
1. There are no Prohibits anywhere, so proceed to step 2.
2a. Work along the Forum row. We get to the last column. There are an equal number of Prevents and Allows here, so jump down to the start of the next row with role assignments.
2b. Work along the Subcategory B row. We get to the second column that corresponds to the overrides in the Course context. There are an equal number of Prevents and Allows here, so jump down to the start of the next row with role assignments.
2c. Work along the System context row. We get to the last column. There are more allows than prevents here, so the final answer is Yes, this user can reply to a post in this forum.


===When overriding permissions===
The path the algorithm takes is shown by the path in this diagram:


... leave the permission for every capability as Inherit (N) apart from the few you want to change. For those, use Allow (A) for things you want to allow, and Prevent (P) for things you want to prevent.
[[Image:Role_calc_table2.png]]


===Only use prohibit in special roles===
====Some variations to our example====


Prohibit exists to cover extreme scenarios such as the following: Suppose you have a naughty student who is posting inappropriate content in your Moodle site. You need to be able to remove their ability to contribute to public discussions until they have promised not to do it again, but during that time, you cannot completely block them from the resources, quizzes, etc. because they must keep up with their studying. Therefore, you need a way to block their access to specific capabilities e.g.  mod/forum:replypost in a way that cannot be overridden. You can do this by creating a special 'Naughty student' role defined with the required permission(s) set to prohibit. Then when you are having problems with a user, you can temporarily assign them this role, either at site level or at course level.
TODO


==A practical example==
==Does this calculation make sense?==


Suppose that a user, who is assigned the role of Course creator in category B, creates a course (becoming Teacher in the course) and then creates a Lesson within the course.  The user is about to edit the lesson (editing lessons is controlled by capability ''mod/lesson:edit''). Here is the permission data:
In almost every situation, the complex tie-breaking rules will not be required. Instead, the rules above will just calculate the answer you would expect if you don't think about it too deeply.


For example, you might be a student in a course and an Authenticated user in the System context. In that case, the calculation becomes:
* Does the student role actually determine the answer? That is, is there an override or does the role definition say whether this capability is Allowed or Prevented. If so, the most specific override gives the answer.
* Otherwise, is this allowed by the Authenticated user role?


    0    System      <---- define Auth user(N), Creator(N), Teacher(A)
===Simple rules for using roles===
            |              assign Auth user
            |
    1  Category A
            |                         
            |                         
    2  Subcategory B  <---- assign Creator
            |
            |
    3    Course      <---- assign Teacher
            |
            |
    4    Lesson      <---- user attempts to edit lesson


We set up the table and populate it with permission data.
If you follow the following guidelines, then the result of has_capability will always follow your intuitive expectations. (If you have a real-life situation where it is not possible to stick to these guidelines, then please post about it in the [http://moodle.org/mod/forum/view.php?id=6826 Roles and Capabilities forum].)


        AuthUser  Creator  Teacher
* When you define a role, just use permissions Allow or Not set (unless you actually need Prohibit).
      +----------+--------+--------
* When you override permissions, only use permissions Allow and Prevent where you actually need them. Leave everything else set to inherit.
    0 |    N    |  N    |    A 
* Assign each user at most one role in each context (except, perhaps, in the System context).
    1 |          |        |
    2 |          |        |
    3 |          |        |     
    4 |          |        |


Since there are no overrides, the table only has data in the first row.  The algorithm quickly calculates a permission of ''A'' and returns true.  The user is allowed to edit the Lesson!
===When to use prohibit===


Now suppose Teacher is overridden in the Lesson context with ''mod/lesson:edit = P''. Then the table changes
Prohibit is designed for one particular situation.


        AuthUser  Creator  Teacher
Suppose a particular user has posted some inappropriate content in a forum. While you investigate what happened, and possibly for a disciplinary period after that, you want to prevent that user from posting anything else.
      +----------+--------+--------
    0 |    N    |  N    |    A
    1 |          |        |
    2 |          |        |
    3 |          |        |       
    4 |          |        |    P               


and clearly the user is no longer allowed to edit the Lesson.
To do this, you create a special 'No posting' role, where a range of capabilities like mod/forum:replypost are set to Prohibit. Then you assign that role to that bad user in the system context. That will ensure that, no matter how the other roles are define, or what role overrides have been set, that user will not be able to post to any forums.


But suppose the administrator (or whoever is making the overrides) decided instead to override the ''Creator role'', setting ''mod/lesson:edit = Prevent'' in the Categoy B context:
==Some practical examples==


        AuthUser  Creator  Teacher
TODO
      +----------+--------+--------
    0 |    N    |  N    |    A
    1 |          |        |
    2 |          |  P    |
    3 |          |        |     
    4 |          |        |                     


Much to everyone's surprise, the user is still allowed to edit the Lesson!  If you understood this article, you shouldn't be surprised, and you should be able to explain what happened.
Checks whether the current logged in user can update a course
<pre>
$course = get_record('course', 'id', optional_param('id',1));
$context = get_context_instance(CONTEXT_COURSE, $course->id);
if (has_capability('moodle/course:update', $context)) {
  /** do stuff here */
}
</pre>


==See also==
==See also==


*Using Moodle [http://moodle.org/mod/forum/discuss.php?d=90140 Logged in: what role am I?] and [http://moodle.org/mod/forum/discuss.php?d=66782 What happens if a user has multiple roles in a course?] forum discussions. 
Forum threads:
* [http://moodle.org/mod/forum/discuss.php?d=90140 Logged in: what role am I?]
* [http://moodle.org/mod/forum/discuss.php?d=66782 What happens if a user has multiple roles in a course?]


There is a script called rolesdebug.php that you can use to create tables like the ones in this article.  It is very useful for debugging roles-related problems. See [[The rolesdebug.php roles debugging script]].
* [[Development:Roles_administration_improvements_for_Moodle_2.0|Roles administration improvements for Moodle 2.0]]
* [[The rolesdebug.php roles debugging script]]


[[Category:Roles]]
[[Category:Roles]]


[[fr:Comment les permissions sont calculées ?]]
[[fr:Comment les permissions sont calculées ?]]
[[cs:Jak se vypočítávají oprávnění]]

Latest revision as of 02:35, 30 July 2010


A frequently asked question is, "What are my permissions in this context?". This article attempts to explain how Moodle answers that question. The article is written for non-programmers as well as programmers. It describes what the function does, not how it does it. If you are a programmer and want to see the how, look at the functions has_capability_in_accessdata and has_capability() in lib/accesslib.php.

Incidentally, you will be able generate permission tables like the ones in this article using the 'Explain' links on the new Check permissions page in Moodle 2.0. In earlier version, you can install the rolesdebug.php roles debugging script.


Clarifying the question

Let us refine the question "What are my permissions in this context?"

Naturally, my can be any user that Moodle knows about. If you don't specify a particular user, it will default to the currently logged in user.

Permissions can be broken down into a computed permissions of true or false for each capability. The different capabilities are completely independant (apart from the magic 'moodle/site:doanything' capability which will be explained later).

A context is a part of a Moodle site. For example a course, a course category, an activity, or so on. Contexts are nested, so an activity will be in a course, which will be in one or more categories, which will be in the whole system. The most specific context that you are in is the most important, and is the one that should be used when asking the question. The other, less-specific contexts that contain this one also influence the result.

So, while our original question was "What are my permissions in this context?" the question Moodle will answer is "Does user $user have capability $capability in context $context?" and the answer will be 'Yes' or 'No'.


The calculation

As explained in the previous section, we want to know whether has_capability($capability, $context, $user) returns true or false.

In order to calculate the answer, Moodle needs to combine various pieces of data.

Data used by the function

Relevant contexts

To simplify the explanation, let us work with a particular example. Suppose that user $user wants to reply to a forum post ($capability is 'mod/forum:replypost') in a particular forum. And suppose that the forum is in a course in Subcategory B which is in Category A in this Moodle site. That is, the relevant contexts are:

      System
        |
    Category A
        |					      
   Subcategory B
        |
      Course
        |
      Forum  <--- the user is here

Role assignments

All the roles that the user has been assigned in all the relevant contexts will influence the outcome of the has_capability question.

A natural example would be that a user has the role 'Student' assigned in context 'Course', and role 'Authenticated User' assigned in context System.

However, our example is going to be unnatural and complex, in order to demonstrate all the possible complexities. In our example the user has

  • Role R1 in context System
  • Roles R2 and R3 in context Subcategory B
  • Roles R1 and R4 in context Forum

Role definitions

Naturally, the definition of each role affects the outcome of has_capability. (Role definitions are edited at Administration ► Users ► Permissions ► Define roles.) The role definition sets a permissions of 'Not set', 'Allow', 'Prevent' or 'Prohibit' for each capability.

As mentioned above, different capabilities are independent, so, for example, we only need to specify the settings for the 'mod/forum:replypost' capability.

  • For role R1, 'mod/forum:replypost' is set to Allow.
  • For roles R2 and R3, 'mod/forum:replypost' is Not set.
  • For role R4, 'mod/forum:replypost' is set to Prevent.

Remember, this is an intentionally complex and unnatural example.

Permission overrides

In Moodle you can override the permissions for a role in a particular context. You can think of that as changing the definition of the role in that context. An example would be "In this category of courses, we want to prevent students from posting to forums." In the absence of an override to a different permission, you can imagine that there is an override of 'Inherit' which means use the permission from higher up the chain of contexts.

In our ongoing example, we will assume the following overrides have been set up:

  • In context Course,
    • for role R2, the permissions for 'mod/forum:replypost' is overridden to Prevent
    • for role R3, the permissions for 'mod/forum:replypost' is overridden to Allow.

Representing the data in tabular form

All the data mentioned in the previous section is best seen by writing it in a table.

In the table, there will be one row for each context, and at the start of each row, we write the name of the roles assigned in that context.

And there will also be one column for each context, and in that column we write any permission overrides for the roles in each row.

Except that in the column that represents the System context, we write the permissions from the role definition.

Normally, we think of contexts like folders in a file system, with the System context at the top, and the Forum context at the bottom. However, for this table, it turns out to be simpler later if we reverse the normal order, and put the Forum context in the first row and column, and the System context at the end.

The table for the example we have been building up looks like this:

Role calc table1.png

The calculation

The calculation proceeds by working through the table in normal reading order, left to right, top to bottom. (That is why we put the Forum context first and the System context last.)

  1. First, look through the entire table and see if there are any Prohibit permissions anywhere. If there are, stop. The final answer is false - the user does not have this capability.
  2. If there are no Prohibits, start at the beginning of the first row with role assignments, and go along until you get to a cell containing 'Allow' and/or 'Prevent'. In this cell,
    • If there are more Allows than Prevents, stop. The answer is true - the user has this capability.
    • If there are more Prevents than Allows, stop. The answer is false - the user does not have this capability.
    • If the number of Prevents and Allows is equal, jump down to the start of the next row with role assignments, and repeat this step 2.
  3. If you get to the end of the table without passing a cell with different numbers of Prevents and Allows, then the answer is false - the user does not have this capability.

Calculating the answer to our example

1. There are no Prohibits anywhere, so proceed to step 2. 2a. Work along the Forum row. We get to the last column. There are an equal number of Prevents and Allows here, so jump down to the start of the next row with role assignments. 2b. Work along the Subcategory B row. We get to the second column that corresponds to the overrides in the Course context. There are an equal number of Prevents and Allows here, so jump down to the start of the next row with role assignments. 2c. Work along the System context row. We get to the last column. There are more allows than prevents here, so the final answer is Yes, this user can reply to a post in this forum.

The path the algorithm takes is shown by the path in this diagram:

Role calc table2.png

Some variations to our example

TODO

Does this calculation make sense?

In almost every situation, the complex tie-breaking rules will not be required. Instead, the rules above will just calculate the answer you would expect if you don't think about it too deeply.

For example, you might be a student in a course and an Authenticated user in the System context. In that case, the calculation becomes:

  • Does the student role actually determine the answer? That is, is there an override or does the role definition say whether this capability is Allowed or Prevented. If so, the most specific override gives the answer.
  • Otherwise, is this allowed by the Authenticated user role?

Simple rules for using roles

If you follow the following guidelines, then the result of has_capability will always follow your intuitive expectations. (If you have a real-life situation where it is not possible to stick to these guidelines, then please post about it in the Roles and Capabilities forum.)

  • When you define a role, just use permissions Allow or Not set (unless you actually need Prohibit).
  • When you override permissions, only use permissions Allow and Prevent where you actually need them. Leave everything else set to inherit.
  • Assign each user at most one role in each context (except, perhaps, in the System context).

When to use prohibit

Prohibit is designed for one particular situation.

Suppose a particular user has posted some inappropriate content in a forum. While you investigate what happened, and possibly for a disciplinary period after that, you want to prevent that user from posting anything else.

To do this, you create a special 'No posting' role, where a range of capabilities like mod/forum:replypost are set to Prohibit. Then you assign that role to that bad user in the system context. That will ensure that, no matter how the other roles are define, or what role overrides have been set, that user will not be able to post to any forums.

Some practical examples

TODO

Checks whether the current logged in user can update a course

$course = get_record('course', 'id', optional_param('id',1)); 
$context = get_context_instance(CONTEXT_COURSE, $course->id);
if (has_capability('moodle/course:update', $context)) {
  /** do stuff here */
}

See also

Forum threads: