<h1>GenericUserFolder</h1>

<p>The GenericUserFolder is a roll-your-own user folder. It provides hooks
to allow you to customize your Zope authentication and authorization the
way you need it.

<h2>Hooks</h2>

<p>
<table border=1>
<tr>
<th>Method Name</th>
<th>Method Parameters</th>
<th>Method Description</th>
<td>Output format</th>
</tr>

<tr>
<td>userExists</td>
<td>username</td>
<td>Returns '1' if the specified username is valid. This method is not
 needed for small numbers of users, but sites with large user populations
 should implement this optional hook</td>
<td>
    Integer '1' or '0'. Make sure you return an integer (<code>
    &LT;dtml-return "_.int(1)"&GT;</code>).
</td>
</tr>

<tr>
<td>userList</td>
<td><em>None</em></td>
<td>Retrieve a list of valid usernames</td>
<td><ul>
    <li>Usernames seperated by whitespace
    <li>A sequence of strings
    <li>A sequence of objects with a 'username' attribute
    <li>A sequence of objects with a 'uname' attribute
    </ul>
</td>
</tr>

<tr>
<td>userRoles</td>
<td>username</td>
<td>Return list of roles for the given user</td>
<td><ul>
    <li>Roles seperated by whitespace
    <li>A sequence of strings
    <li>A sequence of objects with a 'role' attribute
    <li>A sequence of objects with a 'r' attribute
    </ul>
</td>
</tr>

<tr>
<td>userAuthenticate</td>
<td>username, password, roles</td>
<td>Authenticate a user</td>
<td>The integer value '0' for failure, or '1' for success.
    Make sure you return an integer (<code>
    &LT;dtml-return "_.int(1)"&GT;</code>).
</td>
</tr>

<tr>
<td>userDomains</td>
<td>username</td>
<td>Return a list of domains the user can log in from</td>
<td><ul>
    <li>Domains seperated by whitespace
    <li>A sequence of strings
    <li>A sequence of objects with a 'domain' attribute
    <li>A sequence of objects with a 'd' attribute
    </ul>
</td>
</tr>

<tr>
<td>docLogin</td>
<td><em>None</em></td>
<td>The initial login screen</td>
<td>Should contain a form that POSTs the _gufauth_name & _gufauth_password 
parameters.</td>
</tr>

<tr>
<td>docLogout</td>
<td><em>None</em></td>
<td>The screen displayed on logout</td>
<td><em>None</em></td>
</tr>

<tr>
<td>docLoginSuccess</td>
<td><em>N/A</em></td>
<td>The default location docLogin redirects to</td>
<td>This document can initialize
a users session (as the user is now authenticated). It generally
does a redirect to the real document originally requested. The double
redirection is required in some pathalogical cases where the original
document will return different results if requested via POST (as if we
just submitted the login form) or GET (as if we have already authenticated
and are passing a cookie).</td>
</tr>

</table>

<p>These hooks can be DTML methods, or any other Zope method that
uses or can use the same calling convention as DTML methods (eg. External
methods or Python methods). The hooks are called in this format: 
<code>gufHook(GUFFolder,REQUEST,parameter=value)</code>.

<p>Any other Zope method (eg. ZSQL methods, External methods or Python methods)
can be called by using a DTML method as glue. If you reference another method 
or object from a hook, the hook needs to be given a proxy role with sufficient 
rights to access this other method. <strong>Failing to do this correctly is
currently the number one problem encountered setting up a GUF</strong>. 
Generally, a proxy role of 'Manager' is used. For example, to do Radius 
authentication, I need a ZRadius object ('MyRadius') and a DTML method 
('userAuthentication'). The DTML method simply would contain the following:
    &LT;dtml-return "MyRadius(username=username,password=password)"&GT; The
ZRadius object should only be accessible to the Manager role for obvious
security reasons. The DTML method will need to be granted a proxy role of
Manager in order to access the ZRadius object.

<p>Note that changes to these methods, or methods called by these methods, 
may not take effect until you have manually flushed the cache. You may wish
to set the cache times down to a few seconds during development of your
hooks.

<h2>Security</h2>

<p>Few rights are needed on the acl_users folder in order to log in. To ensure
that no one can access your security information, you should keep all the 
hooks, methods and objects required for authentication inside the acl_users 
folder and perform the following steps:

<ol>
<li>Go to the Security tab on the management screen of your GenericUserFolder.
<li>Uncheck any 'Acquire permission settings?' checkboxes that are selected.
<li>Ensure that the Manager role has access to all permissions.
<li>Ensure that no other roles have been granted any permissions.
<li>Grant the Anonymous role 'Login & Logout' permissions.
<li>Grant the Anonymous role 'Access contents information' permissions.
<li>Click on the 'Change' button down the very bottom.
</ol>

<p>Granting 'Login & Logout' permissions to anonymous allows access to the
login and logout screens.

<p>Granting 'Access contents information' permissions is required to allow
code like <code>AUTHENTICATED_USER.getUserName()</code> or 
<code>AUTHENTICATED_USER.getRoles()</code>
to run. Don't grant this permission if you want to restrict a users ability
to see this information. Even if you do this, users can access the username 
by using <code>_.str(AUTHENTICATED_USER)</code>. This permission can be
granted to other roles instead of anonymous if you want.

<p>It is possible to use aquisition to locate hooks (eg. sharing a ZSQL DA in
the root folder amongst a number of GenericUserFolders), but you will need to
be careful about permissions on these objects.

<p>Don't give your users access to create GenericUserFolders. It is a trivial
excercise to steal passwords if you have this permission.

<p>Feel free to remove permissions on objects created in the acl_users
folder until your paranoia is satisfied.

<h2>Login/Logout screens</h2>

<p>The GenericUserFolder has methods called 'login' and 'logout' which
expire the cached authentication information and call the 'docLogin' and
'docLogout' hooks respectivly.

<p>If the URL to the folder containing acl_users is 
'http://www.example.com/subsite', then the URL to the logout screen is
'http://www.example.com/subsite/acl_users/logout' and the URL to the login
screen in 'http://www.example.com/subsite/acl_users/login'. The login screen
will automatically be displayed as soon as a user tries to access a protected
resource in the 'subsite' folder or below.

<h2>Debug mode</h2>

<p>If you are running Zope in Debug mode (with the -D option specified on
the command line, which is the default with the Zope 2.0 distributions),
then you will see traceback errors on your login screen. If you are not
running in Debug mode, then these tracebacks will not be visible (they
are still there, but hidden in comments). This is due to the rather odd
method that has to be used to display this screen if an unauthorized user 
tries to access a protected resource. Accessing the login screen URL
directly displays a login page without these tracebacks.

<h2>Common Errors</h2>

<dl>

<dt>My login screen is throwing an exception
<dd>This is not actually a problem if you are running Zope in debug mode (see Debug Mode earlier in this document). Note that a default install of Zope will be
running in debug mode.

<dt>I can't login as user jorge
<dd>Have a look at userRoles and see what roles jorge belongs to. The document
you are trying to access needs rights granted to one of these roles, or you
can edit userRoles to use a role that exists on your installation such as
Manager.

<dt>I've set up my roles, and permissions on my documents, but still cant
access them
<dd>Note that roles are case sensitive in Zope. Check that the user you
are logging in as has been granted the correct roles using the 
GenericUserFolder's management tabs. You might want to
turn debugging on by editing GenericUserFolder.py (Uncomment the _debug method) 
to see what roles the document actually requires to be viewed.

<dt>GUF locked me out of my site.
<dd>This seems to only happen to the brave souls to manage to install
GUF as a root acl_users using unsupported Zope magic. There should no longer
be any cases which a higher level Manager or the superuser can't reverse.

<dt>I can't get my userList (or other hook) working
<dd>If your hook needs to access another Zope object (such as querying a
ZSQL method), you will need to grant a proxy role to your hook. This is
done by selecting the hook in the management view and going to the Proxy
panel.

</dl>

<h2>Subclassing GenericUserFolder</h2>

<p>It should be possible to subclass GenericUserFolder and hardcoding your
hooks as standard python methods rather than Zope persistant objects.
In theory, all that should need to be done is to override the 
<code>_templateFile(self,file,title,template)</code> method to do nothing, 
and create the <code>userList(self,self2)</code>,
<code>userExists(self,self2,username)</code>,
<code>userAuthenticate(self,self2,username,password,roles)</code> etc. methods.
This has not been tested, but I'm sure someone will write a how-to or
let me know what further changes need to be made to support this (hint hint).

<h2>Future</h2>

<p>Eventually, the GenericUserFolder will be replaced by the LoginManager
being developed as part of the Zope Portal Toolkit. Judging by the
current design, migrating from GUF to the LoginManager should be a 
trivial task, no features will be lost and new features will be gained.

<h2>Further Documentation</h2>

<p>A How To by Richard Taylor is available on www.zope.org that 
details, step by step, a full GenericUserFolder installation 
authenticating Zope users out of a
<a href="http://www.zope.org/Members/hippy/GUF_SQL_crypt">SQL database 
using encrypted passwords</a>.

<p>A click-by-click <a 
href="http://www.zope.org/Members/Zen/GenericUserFolder/walkthrough>walkthrough
</a> is also available.

<p>Other <a href="http://www.zope.org/Documentation/How-To">How-Tos</a>
and <a href="http://www.zope.org/Documentation/Tips">Tips</a> may also
be available on www.zope.org.

<h2>Todo</h2>

<ol>

<li>Don't force Anonymous role for users - it isn't needed?

<li>Allow specification of cookie name for mutiple GUF installations.

<li>Need a way of automatically attaching as a passwordless user with roles
depending on the domain they are from. At the moment, do this with 
a higher level BasicUserFolder (eg. the root BasicUserFolder).

<li>Hooks to store and retrieve authentication token, rather than insist
on using a dictionary shared between threads, for ZEO compatibility.

</ol>

