permissions.xml

Delivered as text/xml

[ hide source ]

File Contents

<?xml version='1.0' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
               "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % myvars SYSTEM "../variables.ent">
%myvars;
]>
<sect1 id="permissions" xreflabel="Permissions System">
<title>Groups, Context, Permissions</title>

 
<authorblurb>
<para>By Pete Su</para>
</authorblurb>



<sect2 id="permissions-overview">
<title>Overview</title>


<para>
The OpenACS &version; 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
<computeroutput>acs_objects</computeroutput> table can be access-controlled via a
PL/SQL or Tcl interface. The permissions system manages a data model
that then allows scripts to check permissions using another API call.
</para>

<para>
Although object level permissions seems appropriate, no developer or
administrator wants to <emphasis>explicitly</emphasis> set access control
rights for <emphasis>every user</emphasis> and <emphasis>every object</emphasis> on a
site. Therefore, OpenACS has two auxiliary mechanisms for making this
easier:</para>
<orderedlist>
<listitem><para>the Groups system allows users to be grouped together
in flexible ways.</para></listitem>
<listitem><para>the object model defines a notion of
<emphasis>object context</emphasis>, which allows applications to group objects
together into larger security domains.
</para></listitem>
</orderedlist>
<para>The rest of this document discusses each of these parts, and how they fit together with the
permissions system.
</para>


</sect2>

<sect2 id="permissions-groups">
<title>Groups</title>



<para>
OpenACS &version; has an abstraction called a <emphasis>party</emphasis>. Parties have a recursive
definition. We can illustrate how it works with the following
simplified data model. First, we define the <computeroutput>parties</computeroutput>
table, where each party has an email address and a URL for contact
information.
</para>

 

<programlisting>

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

</programlisting>


<para>
Now we define two subtypes of party, one for persons, and one for
groups:
</para>

 

<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
)

</programlisting>


<para>
The <computeroutput>users</computeroutput> table is also defined in this data model as a
subtype of <computeroutput>person</computeroutput>. 
</para>

<para>
Finally, we define two relations, one for group <emphasis>membership</emphasis> and
one for group <emphasis>composition</emphasis>.  
</para>
<para>The composition relation expresses that every member of group A should also be a
member of group B.  This relation allows us to define a hierarchy of
groups.
</para>

<para>
The membership relation maps groups to <emphasis>parties</emphasis>. 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.
</para>

<para>
The groups data model is recursive. Modelling parties as either a
person or a group provides a way to model complex hierarchical groupings of persons and
groups.
</para>

<para>
The full details of the groups data model is beyond the scope of this
tutorial. See <xref linkend="parties"/> or <xref
linkend="groups-design"/> for more details.
</para>

</sect2>

<sect2 id="permissions-permissions">
<title>Permissions</title>

<para>
  NOTE: Much more detailed information about the permissions system
  and how to use it is available in the
  <xref linkend="permissions-tediously-explained"/> document.
</para>

<para>
The permissions data model is a mapping between
<emphasis>privileges</emphasis>, parties and objects. Parties and
objects have already been discussed. Now we focus on privileges.
</para>

<para>
In OpenACS, a privilege describes the right to perform some operation on
some object. Privileges are the basic units out of which we build access
control policies.  For example in the Unix filesystem, access is controlled by granting users some combination of
read, write, or execute privileges on files and directories. In
OpenACS &version;,
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 "admin". Then, when a user is granted "admin" privilege, she is
automatically granted all the child privileges that the privilege
contains. The OpenACS &version; kernel data model defines these
privileges:
</para>
 

<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;

</programlisting>
<para>
Note that a user does not gain admin privileges when granted
read, write, create and delete privileges, because some operations
explicitly require admin privileges. No substitutions.
</para>

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

 </para>

<programlisting>
# SQL code
    acs_permission.grant_permission (
      object_id => some_object_id,
      grantee_id => some_party_id,
      privilege => 'some_privilege_name'
      );

</programlisting>


<para>
Using just these mechanisms is enough for developers and
administrators to effectively define access control for every object
in a system. 
</para>
<para>Explicitly defining permissions to every object individually
would become very tedious.
OpenACS provides object contexts as a means for controlling permissions of a large group
of objects at the same time. 
</para>

</sect2>

<sect2 id="permissions-object-context">
<title>Object Context</title>


<para>
In OpenACS &version;, object context is a scoping
mechanism.  "Scoping" and "scope" are terms best
explained by example: consider some hypothetical rows in the
<computeroutput>address_book</computeroutput> table:
</para>


<informaltable frame="all">
<tgroup cols="5">
<thead>

<row>
<entry>...</entry>
<entry><computeroutput>scope</computeroutput></entry>
<entry><computeroutput>user_id</computeroutput></entry>
<entry><computeroutput>group_id</computeroutput></entry>
<entry>...</entry>
</row>
</thead>

<tbody>
<row><entry>...</entry>
<entry><computeroutput>user</computeroutput></entry>
<entry><computeroutput>123</computeroutput></entry>
<entry> </entry><entry>...</entry></row>

<row>
<entry>...</entry>
<entry><computeroutput>group</computeroutput></entry>
<entry> </entry>
<entry><computeroutput>456</computeroutput></entry>
<entry>...</entry>
</row>

<row>
<entry>...</entry>
<entry><computeroutput>public</computeroutput></entry>
<entry> </entry>
<entry> </entry>
<entry>...</entry>
</row>
</tbody>
</tgroup>
</informaltable>

<para>
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&#39;s public
address book. In this way, the scoping columns identify the security context in
which a given object belongs, where each context is <emphasis>either</emphasis> a
person <emphasis>or</emphasis> a group of people <emphasis>or</emphasis> the general public
(itself a group of people).
</para>

<para>
Every object lives in a single <emphasis>context</emphasis>.  A context is just an
another object that represents the security domain to which the object
belongs. By convention, if an object A does not have any permissions
explicitly attached to it, then the system will look at the
<computeroutput>context_id</computeroutput> column in <computeroutput>acs_objects</computeroutput> and check
the context object there for permissions. Two things control the scope
of this search:</para>
<orderedlist><listitem><para>the structure of the context hierarchy
itself, and 
</para></listitem><listitem><para>
the value of the <computeroutput>security_inherit_p</computeroutput>
flag in each object.
</para></listitem></orderedlist>
<para>If
<computeroutput>security_inherit_p</computeroutput> flag is set to <computeroutput>'t'</computeroutput>, then the automatic search
through the context happens, otherwise it does not. You might set this
field to <computeroutput>'f'</computeroutput> if you want to override the default
permissions in a subtree of some context.
</para>

<para>For an example of how to use context hierarchy, consider the forums
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.  
A reasonable thing to do is to create an object representing a forum,
and point the <computeroutput>context_id</computeroutput> 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&#39;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.
</para>

<para>
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:
</para>

<blockquote>
<graphic fileref="images/context-hierarchy.gif" format="GIF"></graphic>
</blockquote>

<para>
The top two contexts in the diagram
are called "magic" numbers, because in some sense, they are created by default by OpenACS
for a specific purpose. The object <computeroutput>default_context</computeroutput>
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
<computeroutput>security_context_root</computeroutput> has a slightly different role. If
some object has no permissions attached to it, and its value for
<computeroutput>security_inherit_p</computeroutput> is <computeroutput>'f'</computeroutput>, or
<computeroutput>context_id</computeroutput> is null, this context is used by default.
</para>
<para>See the package developer tutorials for examples on how to use
permissions code.
</para>
</sect2>


<sect2 id="permissions-summary">
<title>Summary</title>


<para>
OpenACS &version; defines three separate mechanisms for specifying access control
in applications. </para>
<orderedlist><listitem><para>
The Groups data model allows you to define 
hierarchical organizations of users and groups of users. 
</para></listitem><listitem><para>
The Permissions
data model allows you to define a hierarchy of user rights.
</para></listitem><listitem><para>
The Context hierarchy allows you to define organize default
permissions in a hierarchical fashion.
</para></listitem></orderedlist>
<para>A PL/SQL or Tcl API is
then used to check permissions in application pages.
</para>

<para><phrase role="cvstag">($Id: permissions.xml,v 1.18.2.3 2021/01/05 17:33:40 gustafn Exp $)</phrase></para>

</sect2>

</sect1>