% (C) Copyright International Business Machines Corporation 23 January 
% 1990.  All Rights Reserved. 
%  
% See the file USERAGREEMENT distributed with this software for full 
% terms and conditions of use. 
\subsection{Resource manager}
\label{resource}

When you initialize a Hermes process by calling its initialization
port, you often pass some output ports to give the new process initial
access rights.  In some applications the set of access rights may be
very large, and in some cases it may be open-ended---that is, new
services may be added and you may wish to give the child access to
them without extending the parent-child interface and recompiling both
parent and child.  The Hermes shell is a good example of such an
application.  Since it is intended to run arbitrary programs, it has
no way of knowing which services will be required by any given
program.  Trying to provide all known services via the initialization
port is unwieldy and difficult to extend.

We address this problem by supplying a program called the resource
manager.  A process running this program owns a repository holding a
dynamically varying collection of resources (usually capabilities to
make calls to various services), each identified by a name in the form
of a character string.  The resource manager provides services that
allow other processes to add and remove resources, and to retrieve
resources by name.  Instead of passing a newly created process a fixed
set of capabilities to services, the creating process can pass
capabilities to a resource manager.  The new process can later request
particular resources from the resource manager.  The requested
resources will be delivered if they are in the repository at the time
of the request and if the resource manager determines that the caller
has authority to obtain the ports.  Our approach is based on the
resource repository example in the Hermes Tutorial.

Since resources will in general have different types, they are passed
always wrapped in polymorphs (of type {\tt common!polymorph}) when
being passed between applications and the resource manager.  An
application wishing to add a resource to the repository must {\tt
wrap} its value before calling the resource manager's {\tt Post}
service.  Likewise, a process that retrieves a resource from the
repository will need to {\tt unwrap} it before it can be used.

The resource manager maintains three additional pieces of information
for each resource that it manages:

\begin{itemize}
\item the resource name;
\item the ``owner''---the name of the individual who posted the
resource; and
\item an access control function.
\end{itemize}

When a resource is posted to the repository, all three pieces of data,
along with the resource itself, are supplied as call arguments to the
{\tt post} service.  When a retrieve operation is performed, two
arguments are supplied: a resource name, and a ``parameter'' string.
The retrieval request is handled as follows:

\begin{enumerate}
\item The resource manager attempts to locate a resource with the name
specified in the retrieval request; if there is no such resource, an
exception return is made to the
requestor ({\tt rManager!getResource.notFound}).
\item The resource manager calls the access function associated with
the resource, passing it: (1) the name of the requesting user; (2) a
copy of the resource from the repository; and (3) the parameter string
supplied in the retrieval request.
\item The access function decides whether or not the requestor should
be allowed access to the resource, and if so, returns a resource (a
polymorph) to the resource manager.\label{return-from-access}
\item The resource manager passes the resource returned by the access
function back to the requestor.
\end{enumerate}

Note that the resource returned by the access function in
step~\ref{return-from-access} need not be the resource that was stored
in the repository.  The access function is free to return whatever it
wishes as the resource that will fill the retrieval request.  A
customized resource can be built ``on-the-fly,'' or access to a
capability can be provided through a ``filter'' process so that the
access can be revoked if needed at a later time.

If, in step~\ref{return-from-access}, the access function decides that
the requestor should not have access to the requested resource, it can
make an exception return ({\tt rManager!access.accessDenied}), which
then turns into an exception return ({\tt
rManager!getResource.accessDenied}) to the requestor.

\subsubsection{Using the Resource Manager}

A resource manager normally comes in the form of a record of type {\tt
rManager!rManager}, which contains five component capabilities: {\tt
post}, {\tt get}, {\tt delete}, {\tt insert}, and {\tt remove}.  The
interfaces for these interfaces are all defined in {\tt
interface/rmanager.d}.

If you wish to post a resource, choose or write an access control
function.\footnote{We supply a ``trivial'' access function {\tt
ac\_none}, which will give anybody who asks a copy of the resource.}
Wrap the resource into a polymorph (it usually is an output port but
need not be), and call the {\tt post} service of the resource manager:

\begin{verbatim}
    ...
    ac <- create of process ac_none;
    wrap copy of jokeOfTheDayFn as resource;
    call args.rm.post("joke of the day", resource, ac);
    ...
\end{verbatim}

To retrieve a resource, call the {\tt get} service of the resource
manager, passing the name of the desired resource and whatever
parameter string is required by the access control function guarding
the resource (the {\tt ac\_none} access function ignores the parameter
string, so you can just pass an empty string for resources guarded by
{\tt ac\_none}):

\begin{verbatim}
    ...
    unwrap jokeOfTheDay from args.rm.get("joke of the day", "") {init};
    call jokeOfTheDay(...);
    ...
\end{verbatim}

To delete a resource that you posted, call the {\tt delete} service of
the resource manager with the name of the resource to delete:

\begin{verbatim}
    ...
    call args.rm.delete("joke of the day");
    ...
\end{verbatim}

The {\tt insert} and {\tt delete} services of the resource manager are
artifacts of the ugly blemish of uncopyable objects in Hermes.  The
problem is that the normal resource manager services require that
resources be copied in certain circumstances.  For example, the {\tt
post} service interface indicates that the {\tt resource} argument is
full and is initialized on both the call and the return.  This makes
it necessary for the resource manager to copy the resource in order to
save it in its repository.  In addition, the resource manager always
passes a copy of the requested resource to the associated access
control function when a {\tt get} request is made, so that the
resource can remain in the repository for future requests.  If there
were no other services provided by the resource manager, this would
imply that uncopyable objects could not be posted as resources to the
repository.

Consequently, the {\tt insert} and {\tt remove} services correspond to
the normal {\tt post} and {\tt get} services, but they do not attempt
to copy the resources.  This means that if you post a resource by
means of the {\tt insert} service, the resource will be unititialized
for you following the call.  It also means that only a single
retrieval request can be made by the {\tt remove} service, since the
resource cannot be given away and still remain in the repository for
future requests.

\subsubsection{What About User Names}

User names have been mentioned a few times in discussing the resource
manager, but they have not appeared on any of the resource manager
interfaces.  This is because we have really been discussing the
interfaces seen by ``filtered'' access to the resource manager, rather
than direct access.  The resource manager really exports services as
defined in the {\tt interface/sysRManager.d} definitions module.  Each
interface ({\tt post}, {\tt get}, etc.) in the {\tt sysRManager}
definitions module includes a {\tt userName} argument.

Normally, when a process is given access to a resource manager, it is
not given direct access.  Instead, a ``filter'' process is created
that will serve as an intermediary for all calls to the resource
manager.  The filter is supplied with the direct resource manager
capabilities and with the name of the user whose requests it will
filter, on its initialization call.  That call returns a set of
filtered resource manager ports (corresonding to the {\tt rManager},
not the {\tt sysRManager} interfaces), and it is these ports that are
provided to the application process.  From that point on, the filter
process will translate every incoming call on the filtered interfaces
into an unfiltered call directly to the resource manager, and it will
add the user name it got in its initialization call to each such
request.  Because Hermes is a safe language and capabilities are
unforgeable, the application program cannot circumvent this
arrangement and pose as another user.  Thus the access control
functions are able to trust the identification information
accompanying retrieval requests and make decisions based on it.

\subsubsection{Creating Resource Managers}

To create a resource manager and get direct, unfiltered access to its
services, instantiate and call the {\tt rmanager} process module:

\begin{verbatim}
    declare
      RMInit: sysRManagerInitFn;
      RM: sysRManager;
      ...
    begin
      ...
      RMInit <- create of process rManager;
      RM <- RMInit();
      ...
\end{verbatim}
Given an unfiltered resource manager, to build a system in which no
client of the resource manager can cheat, it's easy to create
filters for given users with the {\tt userRM} program:

\begin{verbatim}
    ...
    filteredRM <- userRMFn#(create of userRM)("Joe", bareRM);
    ...
\end{verbatim}

You can then pass {\tt filteredRM} to any of Joe's processes,
confident that they will not be able to break the security of your
access control functions.

There is a convenience program called {\tt personalRM} that creates a
new resource manager along with a filter for a specified user.  It
performs the above two operations in a single step:

\begin{verbatim}
    ...
    filteredRM <- personalRMFn#(create of personalRM)("Joe");
    ...
\end{verbatim}

Finally, there are facilities for ``chaining together'' multiple
resource managers.  Chaining can be done for both filtered and
unfiltered resource managers.

A chained resource manager is a process that maintains an ordered
table of resource managers of the same type.  Whenever a retrieval
request ({\tt get} or {\tt remove}) arrives, the chained resource
manager offers it to each of the resource managers in its list in
turn, until one of the resources responds with either success or an
{\tt accessDenied} exception.  All other requests ({\tt post}, {\tt
insert}, and {\tt delete}) are sent only to a single resource manager
in the list, as designated when the chained resource manager was
created.

To chain resource managers, collect the component resource managers
together and then instantiate and call the {\tt chainedRM} (or {\tt
chainedSysRM} for unfiltered resource managers) process module:

\begin{verbatim}
    ...
    new RMList;
    insert copy of RM_1 into RMList; -- this is the RM for post requests
    insert RM_2 into RMList;
    ...
    chainedRM <- chainedRMInitFn#(create of chainedRM)(RMList, RM_1);
    ...
\end{verbatim}

The interfaces for the {\tt personalRM}, {\tt chainedRM}, and {\tt
chainedSysRM} programs can be found in the {\tt tools/rmanager}
directory instead of the {\tt interface} directory; {\tt personalRM}
interfaces are in {\tt personalrm.d}, while {\tt chainedRM} and {\tt
chainedSysRM} interfaces are in {\tt chainedrm.d}.
