Forums permissions bug

by Andrew Grumet on 2004-02-03
Reference Symptom Install dotlrn2.0/openacs5.0, create a department, term, subject and class. Create a forum in the class. Add a student to the class. Log in as a student and try to post a forum message. The post is posted but then you are not allowed to see it because you don't have permission.

What's going on

The forums security code is checking to see if the user has permission to "read private data" in the package instance being served, but the student doesn't have it.

From forums-security-procs.tcl, cvs version, line 44 if { ![acs_privacy::user_can_read_private_data_p -user_id $user_id -object_id [ad_conn package_id]] } { return 0 This check comes from the forums module, which is part of OpenACS but can also run under dotLRN. The check is run in both regular OpenACS and in dotLRN, but in regular OpenACS the check quietly returns 1 ("true") because privacy control (PrivacyControlEnabledP kernel parameter) is set to 0. In dotLRN privacy control is set to 1.

But why does the check fail for a student, who should have permission to read private data? Here we have to dig a bit into the permissions hierarchy. When users are added to dotLRN, they are granted permission to read private data if they are not a Guest. The relevant code starts at line 97 of www/admin/user-new-2.tcl in the dotlrn package:

    acs_privacy::set_user_read_private_data \
         -user_id $user_id \
         -object_id [dotlrn::get_package_id] \
         -value $read_private_data_p
Here's what's going on. In this code fragment, permission is being granted on the entire dotlrn installation. But in the previous code fragment, permission is being checked on the forums package instance for a particular class. We can verify the permission grants by performing a SQL query directly against the permissions table:
SQL> select as grantee,
   as object
     from acs_permissions where privilege = 'read_private_data';

GRANTEE                    OBJECT
-------------------------- ---------------------------------
Andrew Grumet              dotLRN
Andrew Student             dotLRN
Test Student               dotLRN
So students are granted permission to read private data on the dotLRN installation, but for some reason this permission isn't cascading down to the forum package instance. Why not? Another SQL query:
select object_id,
       object_type, as object,
from acs_objects
connect by prior context_id = object_id
start with object_id = 5646;
  2    3    4
---------- ---------------- ------------------------------ -
      5646 forums_message   la la la                       t
      2504 forums_forum     Management 101 Forum           t
      2235 apm_package      Forums                         t
      2163 apm_package      Management 101                 t
      2041 management.manag management101                  f
      1373 apm_package      dotLRN                         f
       398 apm_service      Main Site                      t
        -3 acs_object       Object -3                      t
The object representing the Management 101 community isn't inheriting permissions from dotLRN, the object on which read_private_data was granted. Voila!

As for why permissions inheritance is turned off, we can dig into the CVS logs for community-procs.tcl in the tcl subdirectory of the dotlrn package.

revision 1.165
date: 2002/08/15 20:23:35;  author: arjun;  state: Exp;  lines: +8 -1
important permission bug fix to dotlrn_community::new.
new communities should _not_ inherit perms from the
root dotlrn instace, since all dotlrn users have
read on the root dotlrn instance. without this
fix, users will be able to read stuff in comm's
they are not members of.

Proposed solutions

The quick and dirty solution to this problem is to make sure that all non-Guest members of each community are granted read_private_data directly on the community when their membership is added. And to make sure that this privilege is revoked upon revoking membership.

But it strikes me as odd that we need a new privilege (read_private_data) instead of just checking for read.

The SQL query below lists out the granted privileges for a community:

SQL> select object_id,
   as object,
   as grantee,
     privilege from acs_permissions where object_id = 2041 order by 2;

 OBJECT_ID OBJECT            GRANTEE                   PRIVILEGE
---------- ----------------- ------------------------- ---------------
      2041 management101     Members of Management 101 create
      2041 management101     Members of Management 101 read
      2041 management101     Members of Management 101 write
      2041 management101     Admins of Management 101  admin
So all members of a community get read privilege on the community, and because of the way inheritance is set up, this cascades down to basically everything in the community. The problem comes with Guests, who should be able to see some stuff in the community but not other stuff. For Guests, we want permissions to cascade to portlets and objects that don't expose private data, but not otherwise. Whereas for non-Guests, permissions can cascade all the way down without consequence.

As best I can tell, the read_private_data privilege is being used as a workaround in lieu of more careful management of individual object permissions, or possibly because the hierarchy/inheritance model simply isn't a good fit here. As designed here, code wanting to restrict access to private data must check for two privileges instead of one, which is unusual for permissions checks in OpenACS, and probably not in the spirit of the original permissions system design.

Update 2004-02-10

Significant discussion and design iterations are taking place on the OCT list. I'll update this page with a link once I figure out where the OCT archives live.

Here's the link: Guest handling in .LRN