Pluggable Authentication Service

« Return to page index

Plone Developer Manual is a comprehensive guide to Plone programming.

1. Introduction

The Pluggable Authentication Service (PAS) is an alternative to the standard Zope User Folder or the popular Group User Folder (GRUF). PAS has a highly modular design, which is very powerful, but also a lot harder to understand.

PAS is built around the concepts of interfaces and plugins: all possible tasks related to user and group management and authentication are described in separate interfaces. These interfaces are implemented by plugins, which can be selectively enabled per interface.

Plone uses PlonePAS, which extends PAS with a couple of extra plugin types and which adds GRUF compatibility. Since PlonePAS extensions are rarely needed and are subject to change in the next Plone releases this tutorial will only focus on pure PAS features.

2. Features and interfaces

A user folder such as PAS provides a number of different services: it takes care of user authentication, it asks users to login if needed, it allows you to search for users and groups.

In order to make both configuration and implementation simpler and more powerful all these different tasks have been divided into different interfaces. Each interface describes how a specific feature, such as authenticating a user, has to be implemented.

Within PAS plugins are used to provide those features. Plugins are small pieces of logic which implement one or more functions as defined by these interfaces.

This separation is useful for different reasons:

  • it makes it possible to configure different aspects of the system separately. For example how users authenticate (cookies, login forms, etc.) can be configured separately from where user information is stored (ZODB, LDAP, RADIUS, SQL, etc.). This flexibility makes it very easy to tune the system to specific needs.
  • it makes it possible for developers to write small pieces of code that only perform a single task. This leads to code that is easier to understand, more testable and better maintainable.

3. The important interfaces

PAS has a number of interfaces that are important for everyone.

The most important interfaces that you may want to configure are:

Authentication
Authentication plugins are responsible for authenticating a set of credentials. Usually that will mean verifying if a login name and password are correct by comparing them with a user record in a database such as the ZODB or an SQL database.
Extraction
Extraction plugins determine the credentials for a request. Credentials can take different forms such as a HTTP cookie, HTTP form data or the users IP address.
Groups
These plugins determine of which group(s) a user (or group) is a member.
Properties
Property plugins manage all properties for users. This includes the standard information such as the user's name and email address but can also be any other piece of data that you want to store for a user. Multiple properties plugins can be used in parallel, making it possible for example to use some information from a central system such as active directory while storing data specific for your Plone site in the ZODB
User Enumeration
User enumeration plugins implement the searching logic for users.

4. Configuring PAS

There is no Plone interface to configure PAS: you will need to use the Zope Management Interface (ZMI). In the ZMI you will see a *acl_users* folder in the site root. This is your PAS.

If you open the acl_users folder you will see a number of different items. Each item is a PAS plugin, which implements some PAS functionality.



There is one special item: the plugins objects manages all administrative bookkeeping within PAS. It remembers which interfaces are active for each plugin and in what order the plugins should be called.

Let's take a look to see how this works. If you open the plugins object you will see a list of all the PAS interfaces, along with a short description of what they do.

We will take a look at the extraction plugins. These plugins take care of extracting the credentials such as your username and password from a request. These credentials can then be used to authenticate the user. If you click on the Extraction Plugins header you will see a screen which shows the plugins which implement this interface and allows you to configure which plugins will be used and in what order.



In the default Plone configuration there are two plugins enabled for this interface:

  • the credentials_cookie_auth plugin can extract the login name and password from an HTTP cookie and HTTP form values from the login form or portlet
  • the credentials_basic_auth plugin can extract the login name and password from standard HTTP authentication headers.

In the default configuration the cookie plugin takes preference over the basic authentication plugin. This means that credentials from a HTTP cookie will be preferred over credentials form HTTP authentication headers if both are present You can try this by first logging in using standard HTTP authentication in the Zope root and then visiting your Plone site and logging in with a different user there: you will see that the new user is now the active user.

You can change the order of the plugins by clicking on a plugin and moving it up or down with the arrows. Using the left and right arrows you can enable and disable a plugin for this interface.

5. Configuring an individual PAS plugin

In addition to enabling and disabling plugins via the plugins object each plugin can also have its own configuration. You can access this by opening a plugin in the ZMI.

Taking the credentials_cookie_auth as example again you will see the screen for the Activate tab. This tab is mandatory and allows you to enable and disable PAS interfaces for a plugin. This corresponds to the plugin configuration we saw earlier, but does not allow you to change the ordering of different plugins for an interface. If you enable a new interface for a particular plugin, it will be activated and placed last in the list of plugins for a particular interface.

cookie-plugin.jpg

You can also go to the properties tab to edit settings specific for this plugin:

cookie-plugin-properties.jpg

What you can configure will differ per plugin. Some plugins do not have any configurations options, others can be very complex.

6. Concepts

PAS has a few basic concepts that you must understand in order to develop PAS related code.

There are a few basic concepts used in PAS:

credentials
Credentials are a set of information which can be used to authenticate a user. This can be a login name and password, an IP address, a session cookie or something else.
user name
The user name is the name used by the user to log into the system. To avoid confusion between user id and user name this tutorial will use the term login name instead.
user id
All users must be uniquely identified by their user id. A users id can be different than the login name.
principal
A principal is an identifier for any entity within the authentication system. This can be either a user or a group. This implies that it is not legal to have a user and a group with the same id!

7. The user object

Contrary to other user folders, a user does not have a single source in a PAS environment. Various aspects of a user (properties, groups, roles, etc.) are managed by different plugins. To accommodate this, PAS features a user object which provides a single interface to all different aspects.

There are two basic user types: a normal user (as defined by the IBasicUser interface) and a user with member properties (defined by the IPropertiedUser interface). Since basic users are not used within Plone we will only consider IPropertiedUser users.

getId()
returns the user id. This is a unique identifier for a user.
getUserName()
Return the login name used by the user to log into the system.
getRoles()
Return the roles assigned to a user "globally".
getRolesInContext(context)
Return the roles assigned to the user within a specific context. This includes the global roles as returned by getRoles().

8. User creation

PAS uses a multi-phase algorithm to create a user object

  1. An IUserFactoryPlugin plugin is used to create a new user object.
  2. All IPropertiesPlugin plugins are queried to get the property sheets.
  3. All IGroupsPlugin plugins are queried to get the groups.
  4. All IRolesPlugin plugins are queried to get the global roles

9. User factory plugin

PAS supports multiple user types. PAS contains two default user types: IBasicUser and IPropertiesUser. IBasicUser is a simple user type which supports a user id, login name, roles and domain restrictions. IPropertiedUser extends this type and adds user properties.

A user factory plugin creates a new user instance. PAS will add properties, groups and roles to this instance as part of its user creation process.

If no user factory plugin is able to create a user PAS will fall back to creating a standard PropertiedUser instance.

The IUserFactoryPlugin interface is a simple one containing a single method:

def createUser( user_id, name ):

""" Return a user, if possible.

o Return None to allow another plugin, or the default, to fire.
"""

The default PAS behaviour is demonstrated by this code::

def createUser(self, user_id, name):
return ProperiedUser(user_id, name)

10. Properties plugins

Properties are stored in property sheets: mapping-like objecst, such as a standard python dictionary, which contain the properties for a principal. The property sheets are ordered: if a property is present in multiple property sheets only the property in the sheet with the highest priority is visible.

Property sheets are created by plugins implementing the IPropertiesPlugin interface. This interface contains only a single method:

def getPropertiesForUser( user, request=None ):

""" user -> {}

o User will implement IPropertiedUser.

o Plugin may scribble on the user, if needed (but must still
return a mapping, even if empty).

o May assign properties based on values in the REQUEST object, if
present
"""

Here is a simple example:

def getPropertiesForUser(self, user, request=None):
return { "email" : user.getId() + "@ourcompany.com" }

this adds an email property to a user which is hardcoded to the user id followed by a companies domain name.

11. Group plugins

Group plugins return the identifiers for the groups a principal is a member of. Since a principal can be either a user or a group this implies that PAS can support nested group members. The default PAS configuration does not support this though.

Like other PAS interfaces the IGroupsPlugin interface is simple and only specifies a single method:

def getGroupsForPrincipal( principal, request=None ):

""" principal -> ( group_1, ... group_N )

o Return a sequence of group names to which the principal
(either a user or another group) belongs.

o May assign groups based on values in the REQUEST object, if present
"""

Here is a simple example:

def getGroupsForPrincipal(self, principal, request=None):
# Manager can not be itself
if principal=="Manager":
return ()

# Only act on the current user
if getSecurityManager().getUser().getId()!=principal:
return ()

# Only act if the request originates from the local host
if request is not None:
ip=request.get("HTTP_X_FORWARDED_FOR", request.get("REMOTE_ADDR", ""))
if ip!="127.0.0.1":
return ()

return ("Manager",)

This puts the current user in the Manager group if the site is being accessed from the Zope server itself.

12. Roles plugin

The IRolesPlugin plugins determine the global roles for a principal. Like the other interfaces the IRolesPlugin interface contains only a single method:

def getRolesForPrincipal( principal, request=None ):

""" principal -> ( role_1, ... role_N )

o Return a sequence of role names which the principal has.

o May assign roles based on values in the REQUEST object, if present.
"""

Here is a simple example:

def getRolesForPrincipal(self, principal, request=None):
# Only act on the current user
if getSecurityManager().getUser().getId()!=principal:
return ()

# Only act if the request originates from the local host
if request is not None:
ip=request.get("HTTP_X_FORWARDED_FOR", request.get("REMOTE_ADDR", ""))
if ip!="127.0.0.1":
return ()

return ("Manager",)

This gives the current user in Manager role if the site is being accessed from the Zope server itself.

13. Authorisation algorithm

These are the steps the PAS user folder follows in its validate method:

  1. extract all credentials. This looks for any possible form of authentication information in a request: HTTP cookies, HTTP form parameters, HTTP authentication headers, originating IP address, etc. A request can have multiple (or no) sets of credentials.
  2. for each set of credentials found
    1. try to authorise the credentials. This checks if the credentials correspond to a known user and are valid.
    2. create a user instance
    3. try to authorise the request. If succesful use this user and stop further processing.
  3. create an anonymous user
  4. try to authorise the request using the anonymous user. If succesful use this, if not:
  5. issue a challenge

14. Credential extraction

Within PAS credentials are a set of information which can identify and authenticate a user. A users login name and password are for example very common credentials. You may also use an HTTP cookie to track users; if you do so the cookie will be your credential.

PAS user credential extraction plugins to find all credentials in a request. Authentication of these credentials is done at a later stage by seperate authentication plugin.

Writing a plugin

If you want to write your own credential extraction plugin it has to implement the IExtractionPlugin interface. This interface only has a single method:

def extractCredentials( request ):

""" request -> {...}

o Return a mapping of any derived credentials.

o Return an empty mapping to indicate that the plugin found no
appropriate credentials.
"""

Here is a simple example:

def extractCredentials(self, request):
login=request.get("login", None)

if login is None:
return {}

password="request.get("password", None)

return { "login" : login, "password" : password }

This plugin extracts the login name and password from fields with the same name in the request object.

15. Credential authentication

The credentials as returned by the credential extraction plugins only reflect the authentication information provided by the user. These credentials need to be authenticated by an authentication plugin to check if they are correct for a real user.

The IAuthenticationPlugin interface is a simple one:

def authenticateCredentials( credentials ):

""" credentials -> (userid, login)

o 'credentials' will be a mapping, as returned by IExtractionPlugin.

o Return a tuple consisting of user ID (which may be different
from the login name) and login

o If the credentials cannot be authenticated, return None.
"""

Here is a simple example:

def authenticateCredentials(self, credentials):
users={ "hanno" : "hannosch", "martin" : "optilude",
"philipp" : "philiKON" }

if "login" not in credentials or "password" not in credentials:
return None

login=credentials["login"]
password=credentials["password"]
if users.get(login, None)==password:
return (login, login)

return None

This plugin allows the users hanno, martin and philipp to login with their nickname as password.

16. Challenges

If the current (possibly anonymous) user is not authorised to access a resource Zope asks PAS to challenge the user. Generally this will result in a login form being shown, asking the user with a appropriately priviliged account.

The IChallengeProtocolChooser and IChallengePlugins plugins work together to do this. Since Zope can be accessed via various protocols (browsers, WebDAV, XML-RPC, etc.) PAS first needs to figure out what kind of protocol it is dealing with. This is done by quering all IChallengeProtocolChooser plugins. The default implementation is ChallengeProtocolChooser, which asks all IRequestTypeSniffer plugins to test for specific protocols.

Once the protocol list has been build PAS will look at all active IChallengePlugins plugins.

Writing a plugin

The IChallengePlugin interface is very simple: it only contains one method:

def challenge( request, response ):

""" Assert via the response that credentials will be gathered.

Takes a REQUEST object and a RESPONSE object.

Returns True if it fired, False otherwise.

Two common ways to initiate a challenge:

- Add a 'WWW-Authenticate' header to the response object.

NOTE: add, since the HTTP spec specifically allows for
more than one challenge in a given response.

- Cause the response object to redirect to another URL (a
login form page, for instance)
"""

The plugin can look at the request object to determine what, or if, it needs to do. It can then modify the response object to issue its challenge to the user. For example:

def challenge(self, request, response):
response.redirect("http://www.disney.com/")
return True

this will redirect a user to the Disney homepage every time he tries to access something he is not authorised for.

17. PAS eats exceptions

A broken user folder is one of the worst things that can happen in Zope: it can make it impossible to access any objects underneath the user folders level.

In order to secure itself against errors in plugins PAS ignores all exceptions of the common exception types: NameError, AttributeError, KeyError, TypeError and ValueError.

This can make debugging plugins hard: an error in a plugin can be silently ignored if its exception is swallowed by PAS.

18. Plugins

Detail about the stock plugins provided by PAS and how to create new ones

18.1. Plugin Interfaces

PAS Plugins are broken down by the different functionalities they provide.

18.1.1. List of Plugin Interfaces

PAS Plugins are broken down by the different functionalities they provide. A particular plugin may provide one or many of the following interfaces

  • Extraction Plugins

    Extraction plugins are responsible for extracting credentials from the request.

  • Authentication Plugins

    Authentication plugins are responsible for validating credentials generated by the Extraction Plugin.

  • Challenge Plugins

    Challenge plugins initiate a challenge to the user to provide credentials.

  • Update Credentials Plugins

    Credential update plugins respond to the user changing credentials.

  • Reset Credentials Plugins

    Credential clear plugins respond to a user logging out.

  • Userfactory Plugins

    Create users.

  • Anonymoususerfactory Plugins

    Create anonymous users.

  • Properties Plugins

    Properties plugins generate property sheets for users.

  • Groups Plugins

    Groups plugins determine the groups to which a user belongs.

  • Roles Plugins

    Roles plugins determine the global roles which a user has.

  • Update Plugins

    Update plugins allow the user or the application to update the user's properties.

  • Validation Plugins

    Validation plugins specify allowable values for user properties (e.g., minimum password length, allowed characters, etc.)

  • User_Enumeration Plugins

    Enumeration plugins allow querying users by ID, and searching for users who match particular criteria.

  • User_Adder Plugins

    User Adder plugins allow the Pluggable Auth Service to create users.

  • Group_Enumeration Plugins

    Enumeration plugins allow querying groups by ID.

  • Role_Enumeration Plugins

    Enumeration plugins allow querying roles by ID.

  • Role_Assigner Plugins

    Role Assigner plugins allow the Pluggable Auth Service to assign

18.2. Plugin Types

A list of the different types of plugins

18.2.1. Extraction Plugins

Extraction plugins are responsible for extracting credentials from the request.

Stock Plugins

The following stock plugins provide the IExtractionPlugin Interface.

Cookie Auth Helper

This plugin helps manage the details of Cookie Authentication. Allows you to extract credentials from a cookie, update them, reset them, etc.

HTTP Basic Auth Helper

Multi-plugin for managing details of HTTP Basic Authentication. Extracts credentials from request and implements the HTTP Auth challenge.

Inline Auth Helper

Manages credentials for inline authentication.

Session Auth Helper

Extracts and manages credentials for session authentication.

Methods

Each plugin implements the following methods:

  • extractCredentials() -- gets credential info from the relevant request, cookie, session, etc.
  • updateCredentials() -- responds to a change of credentials
  • resetCredentials() -- empties out currently stored values

if appropriate, the plugin will also implement a challenge() method which will challenge the user for authentication.

18.2.2. Authentication Plugins

Authentication plugins are responsible for validating credentials generated by the Extraction Plugin.

Stock Plugins

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

Domain Auth Helper

Authenticates users based on their IP address. Has nothing to do with Windows "Domain" Authentication.

ZODB User Manager

ZODB-based user storage. Does authentication, enumeration and properties for users and stores its data in the ZODB.

18.2.3. Challenge Plugins

Challenge plugins initiate a challenge to the user to provide credentials.

Stock Plugins

Cookie Auth Helper

This plugin helps manage the details of Cookie Authentication. Allows you to extract credentials from a cookie, update them, reset them, etc.

HTTP Basic Auth Helper

Multi-plugin for managing details of HTTP Basic Authentication. Extracts credentials from request and implements the HTTP Auth challenge.

Inline Auth Helper

Manages credentials for inline authentication.


18.2.4. Update Credentials Plugins

Credential update plugins respond to the user changing credentials.

Stock Plugins

Cookie Auth Helper

This plugin helps manage the details of Cookie Authentication. Allows you to extract credentials from a cookie, update them, reset them, etc.

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

Inline Auth Helper

Manages credentials for inline authentication.

Session Auth Helper

Extracts and manages credentials for session authentication.

18.2.5. Reset Credentials Plugins

Credential clear plugins respond to a user logging out.

Stock Plugins

Cookie Auth Helper

This plugin helps manage the details of Cookie Authentication. Allows you to extract credentials from a cookie, update them, reset them, etc.

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

HTTP Basic Auth Helper

Multi-plugin for managing details of HTTP Basic Authentication. Extracts credentials from request and implements the HTTP Auth challenge.

Inline Auth Helper

Manages credentials for inline authentication.

Session Auth Helper

Extracts and manages credentials for session authentication.

18.2.6. Properties Plugins

Properties plugins generate property sheets for users.

Stock Plugins

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

18.2.7. Groups Plugins

Groups plugins determine the groups to which a user belongs.

Stock Plugins

Dynamic Groups Plugin

This plugin allows you to create dynamic groups via business rules.

Recursive Groups Plugin

This plugin will recursively flatten a collection of groups.

ZODB Group Manager

This plugin lets you manage groups and groups of groups in the ZODB.


18.2.8. Roles Plugins

Roles plugins determine the global roles which a user has.

Stock Plugins

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

Domain Auth Helper

Authenticates users based on their IP address. Has nothing to do with Windows "Domain" Authentication.

ZODB Role Manager

Stores role information for users in the ZODB. Handles roles storage, role enumeration, and role assignment.

18.2.9. User_Enumeration Plugins

Enumeration plugins allow querying users by ID, and searching for users who match particular criteria.

Stock Plugins

Delegating Multi Plugin

This plugin delegates a PAS interface to some other acl_user folder, typically a "legacy" folder that implements some specific authentication functionality. For example, you can delegate the IAuthenticationPlugin interface to a legacy user folder via a Delegating Multi Plugin.

Search Principals Plugin

Plugin to delegate enumerateUsers and enumerateGroups requests to another PluggableAuthService

ZODB User Manager

ZODB-based user storage. Does authentication, enumeration and properties for users and stores its data in the ZODB.

18.2.10. User_Adder Plugins

User Adder plugins allow the Pluggable Auth Service to create users.

Stock Plugins

ZODB User Manager

ZODB-based user storage. Does authentication, enumeration and properties for users and stores its data in the ZODB.

18.2.11. Group_Enumeration Plugins

Enumeration plugins allow querying groups by ID.

Stock Plugins

Dynamic Groups Plugin

This plugin allows you to create dynamic groups via business rules.

Search Principals Plugin

Plugin to delegate enumerateUsers and enumerateGroups requests to another PluggableAuthService

ZODB Group Manager

This plugin lets you manage groups and groups of groups in the ZODB.

18.2.12. Role_Enumeration Plugins

Enumeration plugins allow querying roles by ID.

Stock Plugins

ZODB Role Manager

Stores role information for users in the ZODB. Handles roles storage, role enumeration, and role assignment.

18.2.13. Role_Assigner Plugins

Role Assigner plugins allow the Pluggable Auth Service to assign

Stock Plugins

ZODB Role Manager

Stores role information for users in the ZODB. Handles roles storage, role enumeration, and role assignment.