Groups & Access Controls

Groups & Access Controls

by Matt Oquist -
Number of replies: 10
Anticipating such a recommendation, I'm moving the following here from the User Authentication forum:

I'm working on an ePortfolio module for Moodle (see discussion here), and to do it right we need comprehensive group support and access controls in Moodle.

I've just had a brainstorm and thought I'd plunk my thoughts down here to get feedback.

Here are my thoughts about groups, which I've already posted in the aformentioned ePorfolio thread:

I don't have time to search the forums ATM but it seems to me
that the best way to add real, honest-to-God group support into Moodle
is to enhance the user_base class (or create one if it doesn't exist)
and add properties such as "users" (list of userids in the group) and
other relevant group-handling functions.

That way groups can be users just like any other user, and the code
changes should be minimal.  Places that currently do things like this:
if ($id == $user->id)

should be changed to do something more like this:
if ($user->matches($id))

$user->id would still be fine, of course, though it should probably
only be set if $user represents a single user.  That would be backwards-compatibile.

That would work whether $user is actually a single person or a group.
We need this before we can do proper access controls.
I'd like to add the additional randomly-accessed thought that we'd need the user-group management code to avoid getting stuck in recursive groups (where groupa has groupb as a member, which has groupc as a member, which in turn has groupa as a member).

Regarding access controls, how about a single table (or set of them if necessary, whatever) that contain all access controls?  Off the top of my head, I'm imagining a table with fields like these:

id
read (bool)
update (bool)
create (bool)
userid (who can access this thing -- this could be a group, of course)
objectid (if the thing to which we're controlling access is represented by a record in the database somewhere, this is that record's ID)
objecttable (see above, this is that record's table)
startdate (when does this access control start to apply?)
enddate (when does this access control stop applying?)
starttime (what time of day does this access control start applying?)
endtime (what time of day does this access control stop applying?)

This can be easily applied to new things, and existing code can begin to take advantage of these access controls as it is updated.
Average of ratings: Useful (1)
In reply to Matt Oquist

Re: Groups & Access Controls

by Matt Oquist -
How about having an "access broker" that centralizes all access control and policy?  If you need to test for access to something, you create a new access_broker object and pass it an access_context object; the broker evaluates your context against policy to determine whether or not you are granted the requested access, and perhaps what kind of access you *are* granted if your request is denied.

Again, shooting from the hip here...


$ab = new access_broker();
$access_context->requested_itemtype = 'course';
$access_context->requested_itemid = '77';
$access_context->requested_action = 'edit';

if ($ab->allow($access_context)) { ... }

$ab = new access_broker();
$access_context->requested_item = 'course';
$access_context->requested_itemid = '77';

$allowances = $ab->allowances($access_context);

if ($allowances['enrol'] === true) {
} elseif ($allowances['view']) {
} else {
  echo "sorry dude";
}

$ab = new access_broker();
$access_context->requested_item = 'assignment_submission';
$access_context->requested_itemid = '827';

$allowances = $ab->allowances($access_context);

if ($allowances['publish']['world'] === true) {
  publish_assignment_submission(827, etc.);
} elseif ($allowances['publish']['request']) {
  contact_advisor_with_publication_request();
} else {
  echo "no publication of anything for you, dude";
}

This would centralize policy and access control not only into one table (or small set of tables) in the database, but into one object (or set of objects) in the code.
In reply to Matt Oquist

Re: Groups & Access Controls

by Martín Langhoff -
> How about having an "access broker" that centralizes
> all access control and policy?

To make a change like that in a system like Moodle you have to know its architecture inside out -- having hacked on it for a long time. It is better to get started writing modules, auth plugings, enrolment plugins, etc.

There is a new access model in the works, and it's being designed by Martin Dougiamas. It is a lot of work -- it not only means very careful design, but also a lot of code changes in all the codebase. Not sure the timeframes, but we're hoping it'll be ready for 1.6 wink
In reply to Martín Langhoff

Re: Groups & Access Controls

by Matt Oquist -
Right - that's why I'm spouting off ideas in forums, rather than coding like mad.  smile  I have to find out what's already going on and try to contribute helpfully if I can.

Is this new access model being designed somewhere where I can look at it?

Also, in the meantime, what I really need is a way for portfolios to behave like they ought to, and I don't know how to do that with Moodle's current access control model.  I'll post more about this back in the portfolios discussion.
In reply to Martín Langhoff

Re: Groups & Access Controls

by Matt Oquist -
Here's my response: http://majen.net/misc/backup-06-01-24.3.tgz.

That's all the portfolio-related code I've been working on, but the access_control part is what's relevant here.

It's a generic access control library for anything (most obviously tables and records in the Moodle database) that has a simple plugin file dropped in the plugins directory.

It includes user-interfaces to let users grant and deny plugin-specific types of access to other users, and APIs to do everything programmatically. There is an administration interface (index.php) to allow easy granting of plugin-specific meta-access to users who are then able to specify certain types of access for other users.

To my knowledge, the last significant piece I need to add before I consider this "phase one complete" is making sure that only users who have been granted the appropriate meta-access can in turn grant the correlated access to other users. (I.e., right now any user can publish anything "to the world", but really only users who have meta_world_read access on a given table should be able to publish things in that table to the world.)

I derived the user interface from the Admin<-->User interface, which is quite good (but all of this could be improved).

You can play with this on my development system. If you log in, join a course, submit an assignment, and then add that assignment to your portfolio, you can click that artifact's "Publish" link to see how it all works.
Here's the system: http://moodle.yi.org:1080/mdev2/

I did this both because I needed it immediately for the portfolio system, but also because so many other things (like file_manager) could really use it. It was my goal that any other module could easily use this library to achieve all of the necessary access control functionality. And, of course, my portfolio module stands as an example.

I'll post the README for access_control as a separate post.
In reply to Martín Langhoff

Re: Groups & Access Controls

by Matt Oquist -
How to develop using the generic Access Control library

This library makes it easy to assign and track ownership and any access controls for the records in any table in the database. (And it wouldn't be difficult to map more complex operations into the fields in the access_control table, so that arbitrary processes can be controlled. Just add a plugin for what you want to access-control and start using the following APIs.)

For an example of code using the library, check out the filter and portfolio modules, specifically the file portfolio/edit.php.

###################################################################
# STUFF YOU GOTTA KNOW
###################################################################
A. All administrators "own" everything.

B. Here are the important properties to pass as an assciative array to the access_control class constructor:
o fkid -- this is the id of an item to which you're controlling access

o ftable -- this is the name of the (foreign) table in which our access-controlled item lives (NOTE: This is only a string, so it needn't refer to a real table. This is actually the name of your access-control plugin, which could be "course backup control" (or whatever unique string) if you like.)

o designee -- this is the userid of the user who is being granted/denied some access to the fkid item in ftable

o access -- this is the numeric representation of the access being granted/denied
The mapping of numeric<->textual representations is stored in the plugins/ subdirectory in a php file named after the specified table/access-control. For example, the portfolio module stores its records in the portfolio_item table, so the access_control/plugins/portfolio_item.php file has the following contents:
<?php
$portfolio_item_display_name = get_string('portfolioitemtableaccessname', 'portfolio');
$portfolio_item_access_types_supplement = array(
'request_publish_to_world' => '1000',
'publish_to_world' => '1001'
);
?>

o yesno -- whether the specified access is being granted (true) or denied (false) for the specified designee

C. Whenever you insert a record into your access-controlled table, you must also insert an ownership record into the access_owner table, which has the following relevant fields:
o fkid -- the id of the item whose ownership you are assigning

o ftable -- the table in which the specified item lives

o owner -- the userid of the owning user

D. Before you update any records in your specified table, you should run the appropriate access-verifying method or function. (Most if not all of the class methods have equivalent library functions that take associative arrays (therefore not requiring you to instantiate an access_control object), and vice-versa.)
Before you delete any records in your specified table, you should run the delete() method or the access_delete(array) function -- this will remove access_control and access_owner records that would be orphaned otherwise. Also, these will return 'false' if the currently authenticated user does not have access to delete the specified item.

Here are the access-verifying functions and class methods:
------------------------------------------
a. can_designee_access() (method)
access_can_designee_access(array) (function)
- Does the specified designee have the specified access to the specified item?

b. can_i_access() (method)
access_can_i_access(array) (function)
- Does the currently authenticated user have the specified access to the specified item?

c. does_designee_own() (method)
access_does_designee_own(array) (function)
- Does the specified designee own the specified item?

d. do_i_own() (method)
access_do_i_own(array) (function)
- Does the currently authenticated user own the specified item?

e. can_designee_chmod() (method)
access_can_designee_chmod(array) (function)
- Does the specified designee have access to change the access controls to the specified item?

f. can_i_chmod() (method)
access_can_i_chmod(array) (function)
- Does the currently authenticated user have access to change the access controls to the specified item?

g. can_designee_delete() (method)
access_can_designee_delete(array) (function)
- Does the specified designee have access to delete the specified item?

h. can_i_delete() (method)
access_can_i_delete(array) (function)
- Does the currently authenticated user have access to delete the specified item?

i. can_designee_update() (method)
access_can_designee_update(array) (function)
- Does the specified designee have access to update the specified item?

j. can_i_update() (method)
access_can_i_update(array) (function)
- Does the currently authenticated user have access to update the specified item?

Here are the access-modifying functions and class methods:
------------------------------------------
k. access_control_page() (method only)
- Display a page with two columns of users, those with the specified access and those without. edit.php in the portfolio module uses this routine and is a good example.

l. get_owner() (method)
access_get_owner(array) (function)
- Return the userid of the owner of the specified item.

m. does_designee_own() (method)
access_does_designee_own(array) (function)
- Does the specified designee own the specified item?

n. do_i_own() (method)
access_do_i_own(array) (function)
- Does the currently authenticated user own the specified item?

o. list_userids() (method)
access_list_userids(array) (function)
- List the users who have the specified access to the specified item.

p. get_users() (method)
access_get_users(array) (function)
- Return objects of the users who have the specified access to the specified item.

q. access_change(bool) (method)
access_change(array, bool) (function)
- Replace or delete access_control records to change the specified designee's specified access to the specified item.
- The boolean parameter indicates whether you are specifying (inserting, replacing) an access control or unspecifying (deleting) an access control.

r. delete() (method)
access_delete(array) (function)
- Safely delete all the access_control and access_owner data for a specified item.


In reply to Matt Oquist

Re: Groups & Access Controls

by Matt Oquist -
Here are some screenshots of the user interfaces provided (generically, for any plugin!) by this library:

interface to grant read access of a portfolio artifact to individual Moodle users

The plugin-specific strings ("Given read access") can be provided in any lang file; they are specified in the plugin definition file.  (There are reasonable defaults though they tend to be vague by necessity and therefore unclear.)

Most of the coolness and flexibility is visible right there, but here are some other screenshots just to give an overview of the different screens and states:

Access Control Administration index page, to select a plugin and grant/deny a type of access to it (there is only one plugin on my system -- the portfolio_item plugin): http://majen.net/misc/mdemo/admin-access-index.png

Clicking on "Designate Moodle-wide publishers" on that page brings you to the following: http://majen.net/misc/mdemo/admin-access-pfitems.png

The user "Monkey See" has granted the whole world read-access to a potfolio artifact, as you can see here: http://majen.net/misc/mdemo/user-publish-world.png

Because Monkey has granted this access, this unauthenticated user can see Monkey's artifact listed: http://majen.net/misc/mdemo/anonymous-view.png

If Monkey wants to revoke the world's read access and publish the artifact only to a few individual Moodle users instead, this will show up after Monkey clicks the "Individual users listed below" radio button:
http://majen.net/misc/mdemo/user-publish-individuals.png
In reply to Matt Oquist

Re: Groups & Access Controls

by Deon Metelski -
Hi Matt,

Your work on the portfolio mod has seen some great progress. I like how you are documenting your thought processes. It seems that you have created a "simple" (not changing the whole base code) of a way to have access control as a user. My question is, as I am following the postings, can this work with what people have been looking for to assign/restrict activities to certain groups/users?

 I have some concerns in regards to portfolios that each user gives world access and then the guest goes to one course, they see everyone who set world access.

I see that in your access control you can assign indivdual users for access. Have you included any groups in your programming?

One last thing, if the answer is yes to the above, can you provide some guidance to maybe implementing this into the other modules?

I know you are busy coding. Thanks for all your great work.

Deon
In reply to Deon Metelski

Re: Groups & Access Controls

by Matt Oquist -
Ack! I thought I had responded to this before now; sorry to keep you waiting.

YES - this is intended to be completely generic. This module can be used to grant and deny user access to absolutely anything - a database record, a databsae table, an activity, a course -- anything! All you need to do is choose a name for the thing you're granting/denying access to (for example, "blog"), and set ftable to that name when you create your access_control objects.

For example:
$myac = new access_control(array('ftable' => 'blog'));

If necessary, you can drop a simple PHP file in the access_control/plugins directory. This plugin file should be owned by whatever module owns the data/activity you're controlling access to.

For example, the portfolio module includes "access_control/plugins/portfolio_item.php" in its tree, and that file looks like this:

<?php
defined('MOODLE_INTERNAL') or die("Direct access to this location is not allowed.");

$portfolio_item_display_name = get_string('portfolioitemtableaccessname', 'portfolio');
$portfolio_item_access_types_supplement = array();

?>

The empty array there is intended to be populated with custom access types for the module; see the $access_types array in contrib/portfolio/contrib/access_control/access_control/lib.php for the standard access types. If your module ("blog" for our example) needs the "comment_on" access type (perhaps to allow a users to attach comments to something), then your module's access_control plugin ("access_control/plugins/blog.php") would have this in it:
$blog_access_types_supplement = array('comment_on' => 2000);

Then you can use the access_control APIs to assign ownership to data owned by your module, and to specify the granting and denial of "comment_on" access to other users.  (I should mention that the plugins file is completely optional.)

As far as guidance on how to use this module in other modules, my previous post, entitled "How to develop using the generic Access Control library", attempts to provide just that guidance. smile

In addition, you can certainly refer to the portfolio module to see how it uses the access_control module.

The portfolio, filter, and access_control modules are currently all available in one bundle, right here.

Also, I'll mention that I'm moving the portfolio discussion to a new thread in a more appropriate place.
In reply to Martín Langhoff

Re: Groups & Access Controls

by Matt Oquist -
Is it possible to have site-wide groups? If so, I can't figure it out. If not, why not?

Hmm. I just went to http://mymoodle/course/groups.php?id=1 and added a group that appears to have worked fine. I must've just not found the link earlier.
In reply to Matt Oquist

Re: Groups & Access Controls

by Joke Scholte -

Hi Matt,

I'am new to Moodle and very enthousiastic about its possibilities. However, I would like to use it to offer the different  schools I work for (and even classes within each school) with specific content. I would like to create a site-wide group for each schoolclass and after that 'attach' courses to each class. This way I will have to set up the groups only once instead  of creating groups for every single course I make. Is this possible in Moodle? If yes, do you have any idea how this works? That is: I know now how to make site-wide groups (thanks for your http://mymoodle/course/groups.php?id= suggestion) but I get stuck in the part of attaching my courses to the groups. Any help is much appreciated!

Joscho (Amsterdam, NL)