<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Groups, Context, Permissions</title><meta name="generator" content="DocBook XSL Stylesheets V1.58.1"><link rel="home" href="index.html" title="OpenACS Documentation"><link rel="up" href="dev-guide.html" title="Chapter�11.�Development Reference"><link rel="previous" href="templates.html" title="Using Templates in OpenACS 4.7.0d"><link rel="next" href="subsites.html" title="Writing OpenACS 4.7.0d Application Pages"><link rel="stylesheet" href="openacs.css" type="text/css"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><a href="http://openacs.org"><img src="images/alex.jpg" border="0"></a><table width="100%" summary="Navigation header" border="0"><tr><td width="20%" align="left"><a accesskey="p" href="templates.html">Prev</a> </td><th width="60%" align="center">Chapter�11.�Development Reference</th><td width="20%" align="right"> <a accesskey="n" href="subsites.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="permissions"></a>Groups, Context, Permissions</h2></div></div><div class="authorblurb"><p><p>By <a href="mailto:psu@arsdigita.com" target="_top">Pete Su</a></p><br>
          OpenACS docs are written by the named authors, and may be edited
          by OpenACS documentation staff.
        </p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-overview"></a>Overview</h3></div></div><p>
The OpenACS 4.7.0d Permissions system allows developers and administrators to
set access control policies at the object level, that is, any
application or system object represented by a row in the
<tt>acs_objects</tt> table can be access-controlled via a simple
PL/SQL or Tcl interface. The permissions system manages a data model
that then allows scripts to check permissions using another simple
API call.
</p><p>
Although this may all sound easy and wonderful, no developer or
administrator would want to <span class="emphasis"><em>explicitly</em></span> set access control
rights for <span class="emphasis"><em>every user</em></span> and <span class="emphasis"><em>every object</em></span> on a
site. Therefore, OpenACS 4.7.0d has two auxiliary mechanisms for making this
easier: First, the Groups system allows users to be grouped together
in flexible ways. Second, the object model defines a notion of
<span class="emphasis"><em>object context</em></span>, which allows applications to group objects
together into larger security domains.  The rest of this document will
talk about each of these parts, and how they fit together with the
permissions system.
</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-groups"></a>Groups</h3></div></div><p>
In OpenACS 3.x, the groups system allowed developers and administrators to
define simple groupings of users. Each group had a human readable name
and unique ID, and there was a single mapping table that mapped users
to groups. (The actual data model was more complicated because it
contained a meta-data system much like the OpenACS 4.7.0d object type system,
but that's not relevant right now.)
</p><p>
The 3.x groups system, while very useful, was limited in few ways. The
main limitation: groups could not either contain or include other
groups. You could not express the fact that all the members of group A
should also be in group B - for example, to model a company with
multiple offices. The company should have a main group representing
all employees, and each office should have a group representing its
employees. Obviously, you'd want every member of an office employee
group to automatically be a member of the whole company employee
group.
</p><p>
In OpenACS 3.x, you also could not express the fact that group A should
itself be a member of group B, without also implying that all of its
members are also members of B. This type of relationship also comes up
in the real world, though not as often. A good example is an
organization like Greenpeace that can have as members both individuals
and organizations: although the Sierra Club may be an organization
member of Greenpeace, its members are not necessarily members of
Greenpeace.
</p><p>
OpenACS 4.7.0d solves both of these modeling problems by introducing a new
abstraction called a <span class="emphasis"><em>party</em></span>. Parties have a recursive
definition, and we can illustrate how it works with the following
simplified data model. First, we define the <tt>parties</tt>
table, where each party has an email address and a URL for contact
information.
</p><pre class="programlisting">

create table parties (
    party_id  integer not null references acs_objects(object_id),
    email varchar(100),
    url varchar(100)
)

</pre><p>
Now we define two subtypes of party, one for persons, and one for
groups:
</p><pre class="programlisting">

create table groups (
    group_id  not null references parties(party_id),
    group_name varchar(100) not null
)

create table persons (
    person_id not null references parties(party_id),
    first_names varchar(100) not null,
    last_name varchar(100) not null
)

</pre><p>
The <tt>users</tt> table is also defined in this data model as a
subtype of <tt>person</tt>. It contains many of the basic columns
that the old OpenACS 3.x <tt>users</tt> table contained.
</p><p>
Finally, we define two relations, one for group <span class="emphasis"><em>membership</em></span> and
one for group <span class="emphasis"><em>composition</em></span>.  The composition relation allows us
to express the fact that every member of group A should also be a
member of group B.  This relation allows us to define a hierarchy of
groups instead of the simple flat groups that 3.x allowed.
</p><p>
The membership relation is much like the mapping we had in 3.x, except
that it maps groups to <span class="emphasis"><em>parties</em></span> instead of groups to users. What
this means is that each member of a group is a party rather than just
a user. That is, groups consist of members that are either a person or
an entire group.  This allows us to say that group A should be a
member of another group B.
</p><p>
The groups data model is pleasantly recursive. The fact that parties
are modeled as being either a person or a group has a lot of power,
allowing us to model complex hierarchical groupings of persons and
groups that were hard or impossible to model in 3.x.
</p><p>
The full details of the groups data model is beyond the scope of this
tutorial - I've just given you what you need to understand how
permissions work. For further detail, you can look at <a href="parties.html">Parties in OpenACS 4</a> or <a href="groups-design.html">OpenACS 4 Groups Design</a>.
</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-permissions"></a>Permissions</h3></div></div><p>
  NOTE: Much more detailed information about the permissions system
  and how to use it is available in the
  <a href="permissions-tediously-explained.html">OpenACS Permissions Tediously Explained</a> document.
</p><p>
The permissions data model is actually pretty simple.  The data model
is a mapping between <span class="emphasis"><em>privileges</em></span>, parties and objects. We
already know what parties and objects are, but we don't know what
privileges are.
</p><p>
In OpenACS 4.7.0d, a privilege models the right to perform some operation on
some object. They are the basic units out of which we build access
control policies.  For example, in the Unix filesystem we typically
implement access control by granting users some combination of
read. write or execute privileges on files and directories. In OpenACS 4.7.0d,
the table of privileges is organized hierarchically so that developers
can define privileges that aggregate some set of privileges
together. For example, if we have read, write, create and delete
privileges, it might be convenient to combine them into a new privilege
called &quot;admin&quot;. Then if we grant a user this privilege she is
automatically granted all the child privileges that the privilege
contains. The OpenACS 4.7.0d kernel data model actually defines these
privileges as follows:
</p><pre class="programlisting">

begin
 acs_privilege.create_privilege('read');
 acs_privilege.create_privilege('write');
 acs_privilege.create_privilege('create');
 acs_privilege.create_privilege('delete');
 acs_privilege.create_privilege('admin');

 acs_privilege.add_child('admin', 'read');
 acs_privilege.add_child('admin', 'write');
 acs_privilege.add_child('admin', 'create');
 acs_privilege.add_child('admin', 'delete');

 commit;
end;

</pre><p>
To give a user permission to perform a particular operation on a
particular object you call
<tt>acs_permission.grant_permission</tt> like this:

 </p><pre class="programlisting">

    acs_permission.grant_permission (
      object_id =&gt; some_object_id,
      grantee_id =&gt; some_party_id,
      privilege =&gt; 'some_privilege_name'
      );

</pre><p>
Using just these mechanisms is enough for developers and
administrators to effectively define access control for every object
in a system. But it would be tedious to explicitly attach permissions
to every object individually: in many cases, we'd prefer controlling
permissions to large groups of objects in the site, all at once. We
use contexts to achieve this goal.
</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-object-context"></a>Object Context</h3></div></div><p>
In OpenACS 4.7.0d, an object context is a generalization of the scoping
mechanism introduced in OpenACS 3.x.  &quot;Scoping&quot; and &quot;scope&quot; are terms best
explained by example: consider some hypothetical rows in the
<tt>address_book</tt> table:
</p><div class="blockquote"><blockquote class="blockquote"><div class="informaltable"><table border="1"><colgroup><col><col><col><col><col></colgroup><thead><tr><th>...</th><th><tt>scope</tt></th><th><tt>user_id</tt></th><th><tt>group_id</tt></th><th>...</th></tr></thead><tbody><tr><td>...</td><td><tt>user</tt></td><td><tt>123</tt></td><td>�</td><td>...</td></tr><tr><td>...</td><td><tt>group</tt></td><td>�</td><td><tt>456</tt></td><td>...</td></tr><tr><td>...</td><td><tt>public</tt></td><td>�</td><td>�</td><td>...</td></tr></tbody></table></div></blockquote></div><p>
The first row represents an entry in User 123's personal address book,
the second row represents an entry in User Group 456's shared address
book, and the third row represents an entry in the site's public
address book.
</p><p>
In this way, the scoping columns identify the security context in
which a given object belongs, where each context is <span class="emphasis"><em>either</em></span> a
person <span class="emphasis"><em>or</em></span> a group of people <span class="emphasis"><em>or</em></span> the general public
(itself a group of people).
</p><p>
In OpenACS 4.7.0d, rather than breaking the world into a limited set of scopes,
every object lives in a single <span class="emphasis"><em>context</em></span>.  A context is just an
another object that represents the security domain to which the object
belongs. By convention, if an object A doesn't have any permissions
explicitly attached to it, then the system will look at the
<tt>context_id</tt> column in <tt>acs_objects</tt> and check
the context object there for permissions. Two things control the scope
of this search: the structure of the context hierarchy itself, and the
value of the <tt>security_inherit_p</tt> flag in each object. If
this flag is set to <tt>'t'</tt>, then the automatic search
through the context happens, otherwise it does not. You might set this
field to <tt>'f'</tt> if you want to override the default
permissions in a subtree of some context.
</p><p> A good example of how to use this hierarchy is in the bboard
application. With only row-level permissions it is not obvious how to
reasonably initialize the access control list when creating a
message. At best, we have to explicitly grant various read and write
privileges whenever we create a message, which is tedious.  In OpenACS 4.7.0d,
a reasonable thing to do is to create an object representing a forum,
and point the <tt>context_id</tt> field of a new message at the
forum. Then, suppose we grant every user in the system read-access to
this forum. By default, they will automatically have read-access to
the new message we just inserted, since the system automatically
checks permissions on the message's context. To allow the creator of
the message to change the message after it has been posted we grant
the user write-access on the message, and we are done.
</p><p>
This mechanism allows developers and administrators to define a
hierarchy that matches the structure they need for access control in
their application.  The following picture shows a typical context
hierarchy for a hypothetical site:
</p><div class="blockquote"><blockquote class="blockquote"><div><img src="images/context-hierarchy.gif"></div></blockquote></div><p>
A few things to note here. First, the top two contexts in the picture
are &quot;magic&quot; in some sense because they are created by default by OpenACS
for a specific purpose. The object <tt>default_context</tt>
represents the root of the context hierarchy for the entire site. All
permission searches walk up the tree to this point and then stop. If
you grant permissions on this object, then by default those
permissions will hold for every object in the system, regardless of
which subsite they happen to live in. The object
<tt>security_context_root</tt> has a slightly different role. If
some object has no permissions attached to it, and its value for
<tt>security_inherit_p</tt> is <tt>'f'</tt>, or
<tt>context_id</tt> is null, then we use this context by default.
</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-example"></a>Example</h3></div></div><p>

At this point, you should either go and download the Notes example
code from the package repository, or check it out of the ArsDigita CVS
repository and add it to your server. The package is called
&quot;notes&quot;. To check it out from CVS, read the <a href="http://acs40.arsdigita.com/acs40-project-central/client-build.html" target="_top">these instructions</a> on how to use anonymous checkouts and then
checkout the module <tt>acs-packages/notes</tt>:

</p><pre class="programlisting">

% export CVSROOT=:pserver:anonymous@cvs.arsdigita.com:/usr/local/cvsroot
% cvs login # the password is acsrules
% cvs checkout acs-packages/notes

</pre><p>
After you have downloaded the package, look at the
<tt>index.tcl</tt> page in the <tt>www</tt> directory. You can also
look at the code <a href="http://cvs.arsdigita.com/cgi-bin/cvsweb.pl/acs-packages/notes/www/index.tcl?rev=1.3&amp;content-type=text/x-cvsweb-markup" target="_top">in your browser</a>. The code should look something like this:
</p><pre class="programlisting">

# main index page for notes.

ad_page_contract {
   @author you
   @cvs-id $Id: permissions.html,v 1.11 2003/06/28 05:07:02 joela Exp $
} -properties {
  notes:multirow
  context_bar:onevalue
  create_p:onevalue
}

set package_id [ad_conn package_id]
set user_id [ad_conn user_id]

set context_bar [ad_context_bar]
set create_p [ad_permission_p $package_id create]

db_multirow notes notes {
  select note_id, owner_id, title, body,
         decode(acs_permission.permission_p(note_id,
                                            :user_id,
                                            'write'),
                't', 1,
                'f', 0) as write_p,
         decode(acs_permission.permission_p(note_id,
                                            :user_id,
                                            'admin'),
                't', 1,
                'f', 0) as admin_p,
         decode(acs_permission.permission_p(note_id,
                                            :user_id,
                                            'delete'),
                't', 1,
                'f', 0) as delete_p
  from notes n, acs_objects o
  where n.note_id = o.object_id
  and o.context_id = :package_id
  and acs_permission.permission_p(note_id, :user_id, 'read') = 't'
  order by creation_date
}

ad_return_template

</pre><p>
This example shows both the Tcl and PL/SQL APIs for checking
permissions.  The Tcl proc <tt>ad_permission_p</tt> and the PL/SQL
function <tt>acs_permission.permission_p</tt> both return a flag
indicating whether the current user has permission to perform the
given action. By default, the Tcl procedure extracts the user_id out
of the request environment, while the SQL procedure takes it as an
argument.
</p><p>
It also shows how we display more complicated items using the template
system. The code here creates a <span class="emphasis"><em>multirow</em></span> data source, i.e. a
data source that represents a query returning multiple rows from the
database. For each row, we return the ID of the note, the ID of the
owner of the note, the title, the body and then three flags that
indicate whether the user has write, admin, and delete
privileges. Also, the WHERE clause of the query ensures that we only
see notes that we are allowed to see.
</p><p>
Next, look at the <a href="http://cvs.arsdigita.com/cgi-bin/cvsweb.pl/acs-packages/notes/www/index.adp?rev=1.1&amp;content-type=text/x-cvsweb-markup" target="_top">index.adp</a>. It is pretty complicated.
The main part of this page uses a <tt>multiple</tt> template
tag. If you want to experiment, you can replace the main body of the
<tt>multiple</tt> tag with this:
</p><pre class="programlisting">

&lt;multiple name=notes&gt;
&lt;td&gt;@notes.title@&lt;/td&gt;&lt;td&gt;@notes.body&lt;/td&gt;
&lt;/multiple&gt;

</pre><p>
This will just make a table with one note per row.
</p><p>
Now put the more complex code back. You'll notice a lot of stuff like this:
</p><pre class="programlisting">

&lt;if @notes.write_p@ eq 1&gt;
  &lt;a href=add-edit?note_id=@notes.note_id@&gt;@notes.title@&lt;/a&gt;
&lt;/if&gt;
&lt;else&gt;
  @notes.title@
&lt;/else&gt;

</pre><p>
This displays the title of the note as either a link or plain text
depending on whether or not we have write privileges on the object.
The <tt>if</tt> tag is something that the OpenACS 4.7.0d template system
defines for you to support conditional presentation. The <a href="/doc/acs-templating/developer-guide.html" target="_top">templates developer guide</a> provides more information about this.
</p><p>
If you study the rest of the system, you will also notice that the
notes application doesn't explicitly attach privileges to the objects
it creates. All privileges are inherited from the context of the object
which in this case is the package instance. The assumption is that the
administrator of the site would create one instance of notes for every
user that wants it, and set the permissions on this instance
appropriately. In a more advanced version of the application, we could
implement a user interface that allowed the user to explicitly attach
permissions to notes that she wanted to make public or whatever. But
that's beyond the scope of this example.
</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="permissions-summary"></a>Summary</h3></div></div><p>
OpenACS 4.7.0d defines three separate mechanisms for specifying access control
in applications. The Groups data model allows you to define 
hierarchical organizations of users and groups of users. The Permissions
data model allows you to define a hierarchy of user rights. Finally,
the Context hierarchy allows you to define organize default
permissions in a hierarchical fashion.  A simple PL/SQL or Tcl API is
then used to do access control checks in application pages.
</p><p>
In the next section, we'll look at a more complex page for adding and
editing notes, and discuss these issues further.
</p><div class="cvstag">($Id: permissions.html,v 1.11 2003/06/28 05:07:02 joela Exp $)</div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="templates.html">Prev</a> </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right"> <a accesskey="n" href="subsites.html">Next</a></td></tr><tr><td width="40%" align="left">Using Templates in OpenACS 4.7.0d </td><td width="20%" align="center"><a accesskey="u" href="dev-guide.html">Up</a></td><td width="40%" align="right"> Writing OpenACS 4.7.0d Application Pages</td></tr></table><hr><address><a href="mailto:docs@openacs.org">docs@openacs.org</a></address></div><a name="comments"></a><center><a href="http://openacs.org/doc/permissions.html#comments">View comments on this page at openacs.org</a></center></body></html>