LoginManager

  LoginManager is a User Folder workalike and replacement which solves
  the 'N * M' problem seen with present User Folders.  Because of it's
  plug-in architecture, it will not need to be subclassed in most
  cases.

  LoginManager uses two kinds of plug-ins, the UserSource and the
  LoginMethod.

  The UserSource provides an interface to an arbitrary store of user
  records.  A LoginManager may contain multiple UserSources.  They are
  tried in user-defined order until one returns a non-None value.

  LoginMethods are responsible for examining a REQUEST for
  identification and credentials.  It is also indirectly responsible
  for authenticating the user.  In most cases, it simply calls
  user.authenticate(), passing the credentials (typically a password)
  it extracted from the REQUEST.  A single LoginManager may contain
  multiple LoginMethods.  The LoginMethods are tried in user-defined
  order, and the result of the last LoginMethod is used.

  UserSource

    UserSources are 'plugged in' to a LoginManager by instantiating
    one in the LoginManager.  The UserSource's manage_afterAdd method
    will register the UserSource with the LoginManager.
    (manage_beforeDelete performs the inverse operation.)

    API

      A UserSource is required to implement the following methods:

        o getUser(self, name)

          Find or construct and return the user object corresponding
          to 'name'.  If not found, return None.

      Technically, getUser is the only method that is required. If you
      know that your User class implements any of the following
      itself, then the US doesn't have to. But normally, the US will
      have to implement these.

        o rolesForUser(self, user)

	  Return a list of the names of the roles 'user' has.  'user'
	  will be an actual user object.

	o domainsForUser(self, user)

	  Return a list of domains, hosts, hostname patterns and IP
       	  addresses that 'user', a user object, is allowed to log in
       	  from.

        o authenticateUser(self, user, password, request)

	  This is called by the user object's authenticate method.
	  This returns a python truth value, with true indicating
	  successful authentication.  'user' is a user object, and
	  'request' is the actual REQUEST.

      The interface for property sheets hasn't materialized yet.  It's
      likely to look like this:

        o sheetsForUser(self, user)

	  This fetches values for propertysheets from the external
	  store.  My initial impression is that it should return a
	  mapping of propertysheet-names to a mapping of property
	  names to property values.  The User object should be
	  responsible for creating the actual propertysheets from this
	  information.  This allows the user object to manipulate the
	  propertysheets, or select propertysheets based on
	  authentication information.

	  PTK Note: The PTK requires that this method is defined.  You
	  may just store the propertysheet information in the ZODB if
	  you wish, but these propertysheets are potentially the most
	  volatile data in a PTK site.  Moving it out to a RDBMS may
	  improve performance at the cost of not being able to undo
	  changes to a user.

  LoginMethod

    LoginMethods are also instantiated in LoginManagers.  A
    LoginManager may have any number of LoginMethods.  The
    LoginManager maintains a list defining the order the LoginMethods
    are tried in, which the user may reorder.

    API

      A LoginMethod is required to implement the following methods:

        o findLogin(self, manager, request, auth, user, roles)

	  findLogin is called by the LoginManager's validate method.
	  It is passed the LoginManager which called it, the web
	  REQUEST, and the auth string passed to validate.  It is
	  expected to examine the request and/or auth string to
	  identify a potential user, fetch that user from the
	  UserStore (via manager) and ask that user to authenticate
	  itself with the information it extracted from request/auth.

	  If successful, it is to return the user object.  Otherwise,
	  it returns None.

	  If this is not the first LoginMethod the LoginManager has
	  tried, 'user' be the result of the previous LoginMethod.
	  This allows custom LMs to veto previous ones, or implement
	  some kind of voting, or stuff extra data into the user
	  object, etc.  LoginManagers which are not interested in such
	  things should immedeately return user if it is non-None.

	  'roles' is the roles that validate() was handed.  (What's it
	  for, again?)


      Optionally, a LoginMethod may define:

        o credentialsChanged(self, manager, user, name, credentials):

	  This method is called when the user changes their password
	  or other information which may require that the browser
	  start submitting new credentials.  This gives the
	  LoginMethod an opportunity to expire/refresh browser
	  cookies, or do anything else that may be necessary to keep
	  the user from being mysteriously logged out.
