The Unenlightened Zopistas Guide to exUserFolder.
(C) 2001-2003 Andrew Milton <akm@theinternet.com.au>

0. INTRODUCTION.

exUserFolder is an extensible authentication product for the Zope
Application Server. It allows a user to choose from a number of
methods of authenticating their users, and allows the setting and
fetching of arbitrary properties on a User object.

Authentication methods, and Property methods do not have to use the
same backing store, so it is possible to use legacy user sources, and
still have configuration information stored about a user.

exUserFolder supports HTTP Basic Authentication, and the so called
Cookie Authentication schemes popular with most webservers.

0.1 Audience.

Everybody, and Nobody. If we build our product correctly, we shouldn't
need user documentation, that's why Nobody. For some reason, normal
sane people seem to lose all of their common sense when sitting in
front of a computer. To the point where plain messages e.g. "Please
Insert a Floppy", evoke a response of "What does that mean?" So that's
why this document is for Everybody.

This is not a guide for writing your own authentication, property, or
membership sources. You need the Zen Masters Guide to exUserFolder
document for that.


1. GETTING STARTED

exUserFolder requires Python 2.1 or above (may work with older
pythons, but, this is not supported). And has been tested on Zope
2.3.0 and above (including 2.4.3).

exUserFolder comes as a source tarball, and it does not rely on any
outside dependencies to be work. Some items may require additional
products to be installed in your Zope tree, but, simply will not
appear if these are not available. Some items may also require
external products to add functionality to them, but, at no time should
the Product not install because of these dependencies.


1.1 Installing exUserFolder.

Unpack exUserFolder in your Products directory. This can be achieved by
executing the following on most UNIX systems.

gzip -d -c exUserFolder-Ma_Mi_u.tgz | tar xvf -

where exUserFolder-Ma_Mi_u.tgz is the version of exUserFolder you have
downloaded.

On systems that have the GNU tar this can be shortened to;

tar zxvf exUserFolder-Ma_Mi_u.tgz

You should restart Zope after unpacking. Installing the product will not
affect your Zope installation in anyway.

If you go to Folder inside your Zope Management Interface, you should see
exUserFolder as a dropdown item near the bottom somewhere.

Congratulations, it's installed.


2. AUTHENTICATION SOURCES AND YOU.

The only mandatory component of an exUserFolder installation, is
choosing an Authentication Source. There are six Authentication
Sources to choose from in the default install. There are other add-on
Sources available from other parties.

Each Authentication Source is different, and assumes at least some
knowledge of that type of authentication scheme.

Most if not all sources store the password encrypted in some
manner. This means that discovering other people's passwords is going
to be more difficult, than with the standard user folder that comes
with Zope.

By default crypt or fcrypt are used, which is are DES encryption
methods.  While this is not the strongest, fastest, choose another
superlative, of encryption techniques, it is certainly adequate for
protecting webpages.

In a later release exUserFolder will allow you to choose what method
is used for password hashing.

Some Authentication Sources can list the users that are available,
some cannot (or will not). Some allow you to add users, and others do
not.  What features are availble depend on the individual
Authentication Source.


2.1 ZODB Authentication Source

The ZODB Authentication Source operates just like a normal User
Folder. It stores its authentication items in the ZODB as the name
suggests. This is the simplest folder to setup, requiring no
parameters.

Choosing ZODB Authentication Source is recommended for testing your install.


2.2 File Based Authentication.

File Based Authentication allows you to have a fixed set of users in a
file with their encrypted passwords. The prerequisites for this are
somewhat convoluted.

In the root of your Zope Installation, on the actual file system (not
in the ZODB), create a directory called exUsers. Make sure that Zope
has access to that directory.

This is the directory where you will create your files.

This is a read only Authentication Source. You will not be able to
create users, or modify their passwords. You can change their roles if you
choose a Property Source.

There are two parameters asked for;

2.2.1 Password File

This is the name of the file that contains your users and passwords.
It should be of the format;

username:cryptedPassword
user2:otherCryptedPasswd

I can contain other fields after the password also delimited by : but these
will not be ussed.

This file should exist inside the exUsers directory.

2.2.2 Default Role

This is the role that all users should be given when the log in. Because this
is a Read Only authentication source, you may not be able to add Roles at a
later date.


2.3 Postgresql Authentication Source

Postgresql Authentication source is an RDBMS backed user store. You
can add, change, and list users. It requires a Postgresql Database
Connection to be created before creating the User Folder.

You should be familiar with databases, and with your schema before
using this Authentication Source. If you don't already have a table
structure in place, a default schema is provided called 'pgScheme.sql'
in the exUserFolder distribution.

The required schema is very simple. You need to store usernames,
passwords, and roles. If your existing schema doesn't support a roles
column you will have to add one.

The configuration scheme looks daunting, but, it is setup to use the
defaults for 'pgScheme.sql' so if you're using this you can safely
continue.

We will run through the items.

2.3.1 Database Connection

If you have any database connections, they will be listed in the drop
down box. Choose the one that represents your connection to your users
table.

2.3.2 Table Name

This is the name of the table containing your users. If you have a
different table to the default, you should change it here.

2.3.3 Username Column

This is the name of the column inside your table that contains the
usernames or logins of your users. This should contain exactly what
the user needs to type in as their username.


2.3.4 Password Column

This is the name of the column inside your table that contains the
encrypted passwords of your users.


2.3.5 Roles Column

This is where the roles are stored. These are used to provide access
to items in Zope.


2.4 User Supplied Authentication Source

This allows you to create your methods in DTML, PythonScripts,
External Methods, or any other callable Zope item for listing,
authenticating, adding and changing your users.

It is beyond the scope of this guide to describe how to do this, but,
the API is quite well defined inside the source, and also in the
README.API document.

This Authentication Source has no configuration parameters.


2.5 RADIUS Authentication Source

This allows you to authenticate your users against a RADIUS server. If
you don't know what this means, then this User Source is not for you
:-)

You will require a RADIUS server to be operating, and for the server
that Zope is running on to have access to it. You will also need to
know the secret key to access the RADIUS server.

2.5.1 Host

This is the host your RADIUS server is running on.

2.5.2 Port

This is the port your RADIUS server is running on. Older installs may
require this to be 1645. The new 'blessed' port by IANA is 1812, and
this is now the default port.

2.5.3 Secret

Every remote host has a secret key it has to share with the server in
order to gain access to the authentication server. You need to know
this.

2.5.4 Retries

Because this is a networked authentication service, errors can
occur. This sets the number of times it will try to authenticate
before giving up.

2.5.5 Timeout

This is how long the RADIUS authenticator will wait for a
response. Because RADIUS operates over UDP, which is a connectionless
protocol, answers may never come back, or never reach their
destination in the first place.

The default is 5 seconds which is actually quite a long time.


2.6 SMB Authentication Source

This source allows you to authenticate your users in a Microsoft
Environment, using the SMB protocols. This is not the same as
authenticating via Directory Services.

If your SMB server requires passwords to be encrypted in transit, you'll 
need to install mxCrypto.

2.6.1 Host

This is the host that your Authentication service is on, this is
normally an NT or Win2K server, but, it can also be a UNIX box running
Samba. This should be the NetBIOS name of the server.

2.6.2 Domain

This is the NT/Windows DOMAIN that the user is to authenticate
against.

2.6.3 WINS Server IP Address (optional)

If provided, this should be the IP address of the WINS server to be
queried to locate your auth host (see 2.5.1 above).

If you leave this field empty, the location of the authentication host
will be queried by broadcast, which works just fine if the Zope
machine is on the same subnet as your auth host but not if the auth
host is across a subnet link or if it's in the same machine as Zope
(don't ask. Apparently, some braindmamaged creature at M$ decided that
a machine shouldn't answer to its own broadcasts no matter what)

Fill in this field if you are getting "NetBIOSTimeout" errors but you
are sure that your auth host was specified correctly, or if Windows
machines in your subnet also use a WINS server.


2.7 LDAP Authentication

This source allows you to authenticate your users against an LDAP
server.  This code is based on the auth_ldap module for Apache. The
documentation for these parameters is unashamedly lifted directly from
the documentation of the Apache directives for auth_ldap.

See: http://www.rudedog.org/auth_ldap/

You must choose a property source when using LDAP Authentication, all
of the properties associated with the LDAP user entry are stored as
properties when they authenticate. Items with multiple entries are
stored as a list of items.

2.7.1 URL

An RFC 2255 URL which specifies the LDAP search parameters to use. The syntax
of the URL is
ldap://host:port/basedn?attribute?scope?filter

ldap      For regular ldap, use the string ldap. For secure LDAP, use ldaps
          instead. Secure LDAP is only available if auth_ldap was compiled with
          SSL support.                                                         
host:port The name/port of the ldap server (defaults to localhost:389 for ldap,
          and localhost:636 for ldaps). 
                        
          Once a connection has been made to a server, that connection remains
          active for the life of the Zope process, or until the LDAP server
          goes down.
                                                                          
          If the LDAP server goes down and breaks an existing connection,
          the Auth Source will attempt to re-connect
                       
basedn    The DN of the branch of the directory where all searches should start
          from. At the very least, this must be the top of your directory tree,
          but could also specify a subtree in the directory.

attribute The attribute to search for. Although RFC 2255 allows a
          comma-separated list of attributes, only the first attribute will be
          used, no matter how many are provided. If no attributes are provided,
          the default is to use uid. It's a good idea to choose an attribute
          that will be unique across all entries in the subtree you will be
          using.                                                              

scope     The scope of the search. Can be either one or sub. Note that a scope
          of base is also supported by RFC 2255, but is not supported by this
          module. If the scope is not provided, or if base scope is specified,
          the default is to use a scope of sub.

filter    A valid LDAP search filter. If not provided, defaults to (objectClass
          =*), which will search for all objects in the tree.

When doing searches, the attribute, filter and username passed by the HTTP
client are combined to create a search filter that looks like (&(filter)
(attribute=username)).

For example, consider an URL of ldap://ldap.xuf.com/o=XUF?cn?sub?(posixid
=*). When a client attempts to connect using a username of The Jester, the
resulting search filter will be (&(posixid=*)(cn=The Jester)).


2.7.2 Bind DN

An optional Distinguished Name user to bind to the server when searching
for entries. If not provided an Anonymous bind will be used.

2.7.3 Bind Password.
A bind password to use in conjunction with the bind DN. Note that the bind
password is probably sensitive data, and should be properly protected. You
should only use the Bind DN and Bind Password if you absolutely
need them to search the directory.

2.7.4 Cert DB Path

Specifies in which directory LDAP Auth Source should look for the certificate
authorities database. There should be a file named cert7.db in that directory.

2.7.5 Compare DN On Server

When set, LDAP Auth Source will use the LDAP server to compare the
DNs. This is the only foolproof way to compare DNs. LDAP Auth Source
will search the directory for the DN specified with the require dn
directive, then, retrieve the DN and compare it with the DN retrieved
from the user entry. If this directive is not set, LDAP Auth Source
simply does a string comparison. It is possible to get false negatives
with this approach, but it is much faster. Note the LDAP Auth Source cache
can speed up DN comparison in most situations.

2.7.6 Dereference Aliases

This directive specifies when LDAP Auth Source will de-reference
aliases during LDAP operations. The default is always.

2.7.7 Group Attribute is DN

When set, this directive says to use the distinguished name of the
client username when checking for group membership. Otherwise, the
username will be used. For example, assume that the client sent the
username tjester, which corresponds to the LDAP DN cn=The Jester,
o=XUF. If this directive is set, LDAP Auth Source will check if the
group has cn=The Jester, o=XUF as a member. If this directive is not
set, then LDAP Auth Source will check if the group has tjester as a
member.

2.7.8 Compare Cache Size

This specifies the size of the cache used to cache LDAP compare
operations. The default is 1024 entries. Setting it to 0 disables
operation caching.

2.7.9 Compare Cache TTL

Specifies the time (in seconds) that entries in the operation cache
remain valid. The default is 600 seconds.

2.7.10 Start TLS

If this is set to Yes, LDAP Auth Source will start a secure TLS
session after connecting to the LDAP server. This requires your LDAP
server to support TLS.

2.7.11 Require User (one per line)

The require user directive specifies what usernames can access the
resource.  Once LDAP Auth Source has retrieved a unique DN from the
directory, it does an LDAP compare operation using the username
specified in the require user to see if that username is part of the
just-fetched LDAP entry. Multiple users can be granted access by
putting multiple usernames in the box, separated with newlines. For
example, with a AuthLDAPURL of ldap://ldap/o=XUF?cn (i.e., cn is used
for searches), the following require entries could be used to restrict
access: The Jester Fred User Joe Manager

Because of the way that LDAP Auth Source handles this directive, The
Jester could sign on as The Jester, Zen Jester or any other cn that he
has in his LDAP entry. Only the single require user line is needed to
support all values of the attribute in the user's entry.

If the uid attribute was used instead of the cn attribute in the URL
above, the above three lines could be;

tj
fred_u
jmanager

2.7.12 Require Group (one per line)

This directive specifies an LDAP group whose members are allowed
access. It takes the distinguished name of the LDAP group. For
example, assume that the following entry existed in the LDAP
directory:

dn: cn=Administrators, o=XUF
objectClass: groupOfUniqueNames
uniqueMember: cn=The Jester, o=XUF
uniqueMember: cn=Fred User, o=XUF

The following directive would grant access to both Fred and Jester:

require group cn=Administrators, o=XUF

Behavior of this directive is modified by the Group Attribute and 
Group Attribute Is DN options.

2.7.13 Require DN

The require dn option allows the administrator to grant access based
on distinguished names. It specifies a DN that must match for access
to be granted. If the distinguished name that was retrieved from the
directory server matches the distinguished name in the require dn,
then authorization is granted.

The following directive would grant access to a specific DN:
require dn cn=The Jester, o=XUF

Behavior of this directive is modified by the Compare DN On Server option.

2.7.14 Default Manager

This allows you to specify the username of the Manager for this area.
The manager will still need to meet auth requirements above, but, if
they do they will get the 'Manager' role added to their list of roles.

2.7.15 Default Role

This is a role to be assigned to users when they auth correctly. This
is to differentiate them from merely being 'authenticated'.

2.7.16 Examples

  * Grant access to anyone who exists in the LDAP directory, using their UID
    for searches.
    URL ldap://ldap1.zope.com:389/ou=People, o=XUF?uid?sub?(objectClass=*)

  * The next example is similar to the previous one, but is uses the common
    name instead of the UID. Note that this could be problematical if multiple
    people in the directory share the same cn, because a search on cn must
    return exactly one entry. That's why this approach is not recommended: it's
    a better idea to choose an attribute that is guaranteed unique in your
    directory, such as uid.
    URL ldap://ldap.zope.com/ou=People, o=XUF?cn

  * Grant access to anybody in the Administrators group. The users must
    authenticate using their UID.
    URL ldap://ldap.zope.com/o=XUF?uid
    require group: 
    cn=Administrators, o=XUF

  * The next example assumes that everyone at XUF who carries an
    alphanumeric pager will have an LDAP attribute of qpagePagerID. The example
    will grant access only to people (authenticated via their UID) who have
    alphanumeric pagers:
    URL: ldap://ldap.zope.com/o=XUF?uid??(qpagePagerID=*)

  * The next example demonstrates the power of using filters to accomplish
    complicated administrative requirements. Without filters, it would have
    been necessary to create a new LDAP group and ensure that the group's
    members remain synchronized with the pager users. This becomes trivial with
    filters. The goal is to grant access to anyone who has a filter, plus grant
    access to Joe Manager, who doesn't have a pager, but does need to access
    the same resource:
    URL ldap://ldap.zope.com/o=XUF?uid??(|(qpagePagerID=*)(uid=jmanager))

    This last may look confusing at first, so it helps to evaluate what the
    search filter will look like based on who connects, as shown below.
    If Fred User connects as fuser, the filter would look like
   
    (&(|(qpagePagerID=*)(uid=jmanager))(uid=fuser))
   
    The above search will only succeed if fuser has a pager. When Joe Manager
    connects as jmanager, the filter looks like
   
    (&(|(qpagePagerID=*)(uid=jmanager))(uid=jmanager))

    The above search will succeed whether jmanager has a pager or not.


2.8 General Items.

You can choose to use standard auth, or cookie auth, and you can
decide how long you want to cache the users credentials before
retrying.

2.8.1 Authentication Type

2.8.1.1 Standard Authentication

This method causes the browser to pop up a dialog box to ask for the
username and password.

2.8.1.2 Cookie Authentication

This method allows you to use a normal HTML form to get the username
and password from the user. It also will present the default form to
the user if they try to access an unauthorised area.


2.8.1.3 Secure Cookie based Authentication

This method, like Cookie Authentication allows you to use a HTML form
to get the user details. However, the cookie it uses does not contain
any login information. It is internally checked against a cache of
hashes and the information is derived from that. This cache disappears
if you restart Zope, so this is not a good option for people who want
to persistently cache logins across sessions.


2.8.2 Credential Cache Timeout in Seconds

exUserFolder by default caches credential information, so that the
authorisation source isn't hit *for every object and page* that has to
be fetched. For remote authentication services this can slow things
down quite considerably. Even setting this to a modest setting will
quicken response times.

Setting this too long could cause problems if you want to lock out a
troublesome user. The credential cache is flushed if someone provides
a password that doesn't match the one in the cache.


2.8.3 Negative Credential Cache Timeout in Seconds

exUserFolder allows you to cache login failures for users that do not
exist. This means you don't have to go out to your auth source when
you know for certain this user is never going to be able to
authenticate.

Due to the way some auth sources are designed, this doesn't work for
auth sources like SMB Auth Source that lie initially about the user
existing (because you can't verify the existence of a user without
authenticating them), and then check the credentials later.

It's possible to create a hybrid auth source that lets this work
correctly for auth sources that can't list the users.


2.8.4 Log out users who expire from cache?

If you've got caching turned on, then this will force any user who has
their session expire to login again. Some people like to do this.


2.8.5 Activate Session Tracking for anoymous users?

For any anonymous user, a new temporary user is created. This allows
you to set/get properties for anonymous users too. Currently
experimental.


3.0 PROPERTY SOURCES

4.0 MEMBERSHIP SOURCES

5.0 TIPS FOR THE UNWARY

Generally these things apply to Cookie Authentication models, since
there is additional access required to present the login form.

5.1 Too much protection.

A lot of people try to protect a folder by placing an exUserFolder
inside.  They then change the permissions on this folder to only allow
Authenticated or some Local Role to have permission.

5.1.1 The problem

When you try to access the folder, instead of getting the form, you
get a popup box, even though you chose Cookie Authentication. Even
when you enter a username and password it doesn't work.


5.1.2 What happened

You tried to access an area you don't have access to. Zope found the
closest user folder to the object you were trying to access. The user
folder decided you were not authorised and tried to display the login
form. You don't have access to view the login form, so Zope finds the
nearest user folder to the login form, which is the user folder above
the protected directory. It pops up the authentication dialog. If you
put in a valid username and password for this top level, then lower
level then displays the login form.

5.1.3 Solution 1 (preferred).

Place the user folder one level *above* the folder you want to protect,
that is in the unprotected area. Everything should work fine.

5.1.4. Solution 2 (not so preferred).

Set the 'View' permission on the docLogin form inside the acl_users
folder.  You can get there by Choosing 'Contents' on docLogin and
scrolling down to the bottom.

6.0 MISCELLANY

6.1 Adding an exUserFolder from a product.

You can add an exUserFolder from a Python product fairly easily, if
not a tad messily.


from Products.exUserFolder.exUserFolder import manage_addexUserFolder, eUserFolder

manage_addexUserFolder(authId='zodbAuthSource', propId='zodbPropSource',
                       memberId='basicMemberSource',
                       cookie_mode=1, session_length=600, REQUEST)

Obviously change authId, propId, and memberId to what you want.
However, you'll need to ram in the appropriate form fields for the various
source constructors into your REQUEST.

6.2 Session Tracking.

Session tracking (currently) relies on having the credential cache
active, and a property source active. Your trackable user will only
last as long as they are not expired from the cache. You should set
the cache expiry length to be somewhat longer than normal if you plan
to use Session Tracking, and you should also be prepared to check that
the current session is valid.
