A presentation given at Plone Conference 2013 in Brasilia. This presentation explains Plone's Pluggable Authentication System (PAS) and how to get started with writing your own PAS Plugin.
The Future of Software Development - Devin AI Innovative Approach.pdf
How to get started with the Pluggable Authentication System
1. How to get started with
Matt Hamilton
The Pluggable
Authentication System
Plone Conference 2013 - Brasilia
2. Plone Conference 2013 - Brasilia
Who am I?
• Working with Plone/Zope since 1999
• Director at Netsight in the UK
• Worked on a number of projects doing
authentication over the years
7. Plone Conference 2013 - Brasilia
Architecture
• Uses the Zope Component Architecture
(ZCA) heavily
• Many interfaces, each defining an aspect of
the authentication process
• Each plugin can implement one or more
interfaces
8. Plone Conference 2013 - Brasilia
Anonymoususerfactory Plugins
Create anonymous users.
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.
Challenge_Protocol_Chooser Plugins
Challenge Protocol Chooser plugins decide what authorizationprotocol to
use for a given request type.
Reset Credentials Plugins
Credential clear plugins respond to a user logging out.
Update Credentials Plugins
Credential update plugins respond to the user changing credentials.
Extraction Plugins
Extraction plugins are responsible for extracting credentials from the
request.
Group_Enumeration Plugins
Enumeration plugins allow querying groups by ID.
Group_Introspection Plugins
Group Introspection provides listings of groups and membership
Group_Management Plugins
Group Management provides add/write/deletion of groups and member
management
Groups Plugins
Groups plugins determine the groups to which a user belongs.
Local_Roles Plugins
Defines Policy for getting Local Roles
Notcompetent Plugins
Not-Competent plugins check whether this user folder should not
authenticate the current request. These plugins are not used for a top
level user folder. They are typically used to prevent shaddowing of
authentications by higher level user folders.
Properties Plugins
Properties plugins generate property sheets for users.
Request_Type_Sniffer Plugins
Request Type Sniffer plugins detect the type of an incoming
request.
Role_Assigner Plugins
Role Assigner plugins allow the Pluggable Auth Service to
assign roles to principals.
Role_Enumeration Plugins
Enumeration plugins allow querying roles by ID.
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.
User_Adder Plugins
User Adder plugins allow the Pluggable Auth Service to create
users.
User_Enumeration Plugins
Enumeration plugins allow querying users by ID, and
searching for users who match particular criteria.
Userfactory Plugins
Create users.
User_Introspection Plugins
The User Introspection plugins allow the Pluggable Auth
Service to provide lists of users
User_Management Plugins
The User Management plugins allow the Pluggable Auth
Service to add/delete/modify users
Validation Plugins
Validation plugins specify allowable values for user properties
(e.g., minimum password length, allowed characters, etc.)
9. Plone Conference 2013 - Brasilia
Interfaces
class IExtractionPlugin( Interface ):
""" Extracts login name and credentials from a request.
"""
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.
"""
10. Plone Conference 2013 - Brasilia
Interfaces
class IAuthenticationPlugin( Interface ):
""" Map credentials to a user ID.
"""
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.
"""
11. Plone Conference 2013 - Brasilia
Interfaces
class IPropertiesPlugin( Interface ):
""" Return a property set for a user.
"""
def getPropertiesForUser( user, request=None ):
""" user -> {}
o User will implement IPropertiedUser.
o Plugin should return a dictionary or an object providing
IPropertySheet.
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
"""
12. Plone Conference 2013 - Brasilia
Interfaces
class IGroupsPlugin( Interface ):
""" Determine the groups to which a user belongs.
"""
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
"""
13. Plone Conference 2013 - Brasilia
Plugins
• Plugins can be stacked in order you want
them to be used
17. Plone Conference 2013 - Brasilia
Worked Example
• netsight.aspxauthplugin
• Encrypts/Decrypts the .ASPXAUTH cookie
used by .NET applications
• Allows Plone to trust the auth of a .NET
application and vice-versa
• Simplified, ignoring some of the boiler plate
and crypto code
18. Plone Conference 2013 - Brasilia
def extractCredentials( self, request )
“””To extract the cookie from the browser”””
def authenticateCredentials( self,
credentials )
“””To decrypt the cookie and validate it is correct”””
def resetCredentials(self, request,
response)
“””To delete the cookie on logout”””
20. Plone Conference 2013 - Brasilia
security.declarePrivate( 'authenticateCredentials' )
def authenticateCredentials( self, credentials ):
request = self.REQUEST
response = request.RESPONSE
# We only authenticate when our challenge mechanism
# extracted the cookie
if credentials.get('plugin') != self.getId():
return None
cookie = credentials.get('cookie')
if not cookie:
return None
sig, data = self.decodeCookie(cookie)
21. Plone Conference 2013 - Brasilia
# check signature is valid
if not self.checkSignature(data,sig):
return None
# decrypt data
decryptedBytes = self.decryptData(data)
if not decryptedBytes:
return None
# unpack the values from the data
unpacked = self.unpackData(decryptedBytes)
if unpacked is None:
return None
start_time, end_time, username, version, persistent,
userdata, path = unpacked
# return the userid and login
return username, username