how to get started with the pluggable authentication system
DESCRIPTION
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.TRANSCRIPT
How to get started with
Matt Hamilton
The Pluggable Authentication System
Plone Conference 2013 - Brasilia
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
Plone Conference 2013 - Brasilia
What is PAS?
• Pluggable Authentication System
Plone Conference 2013 - Brasilia
History of PAS
• Zope User Folders
• ExtUserFolder
• PAS
Plone Conference 2013 - Brasilia
PAS is one of Plone’s Killer Features
Plone Conference 2013 - Brasilia
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
Plone Conference 2013 - Brasilia
Anonymoususerfactory PluginsCreate anonymous users.Authentication PluginsAuthentication plugins are responsible for validating credentials generated by the Extraction Plugin.Challenge PluginsChallenge plugins initiate a challenge to the user to provide credentials.Challenge_Protocol_Chooser PluginsChallenge Protocol Chooser plugins decide what authorizationprotocol to use for a given request type.Reset Credentials PluginsCredential clear plugins respond to a user logging out.Update Credentials PluginsCredential update plugins respond to the user changing credentials.Extraction PluginsExtraction plugins are responsible for extracting credentials from the request.Group_Enumeration PluginsEnumeration plugins allow querying groups by ID.Group_Introspection PluginsGroup Introspection provides listings of groups and membershipGroup_Management PluginsGroup Management provides add/write/deletion of groups and member managementGroups PluginsGroups plugins determine the groups to which a user belongs.Local_Roles PluginsDefines Policy for getting Local RolesNotcompetent PluginsNot-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 PluginsProperties plugins generate property sheets for users.Request_Type_Sniffer PluginsRequest Type Sniffer plugins detect the type of an incoming request.Role_Assigner PluginsRole Assigner plugins allow the Pluggable Auth Service to assign roles to principals.Role_Enumeration PluginsEnumeration plugins allow querying roles by ID.Roles PluginsRoles plugins determine the global roles which a user has.Update PluginsUpdate plugins allow the user or the application to update the user's properties.User_Adder PluginsUser Adder plugins allow the Pluggable Auth Service to create users.User_Enumeration PluginsEnumeration plugins allow querying users by ID, and searching for users who match particular criteria.Userfactory PluginsCreate users.User_Introspection PluginsThe User Introspection plugins allow the Pluggable Auth Service to provide lists of usersUser_Management PluginsThe User Management plugins allow the Pluggable Auth Service to add/delete/modify usersValidation PluginsValidation plugins specify allowable values for user properties (e.g., minimum password length, allowed characters, etc.)
Plone Conference 2013 - Brasilia
Interfacesclass 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. """
Plone Conference 2013 - Brasilia
Interfacesclass 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. """
Plone Conference 2013 - Brasilia
Interfacesclass 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 """
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 """
Plone Conference 2013 - Brasilia
Plugins
• Plugins can be stacked in order you want them to be used
Plone Conference 2013 - Brasilia
Plugins
Plone Conference 2013 - Brasilia
Example PAS Plugins
• ZODB User Manager
• Products.LDAPMultiPlugins (PloneLDAP)
• pas.plugins.velruse
• netsight.windowsauthplugin
• netsight.aspxauthplugin
Plone Conference 2013 - Brasilia
Combining PAS Plugins
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
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”””
Plone Conference 2013 - Brasilia
security.declarePrivate( 'extractCredentials' ) def extractCredentials( self, request ):
""" Extract auth credentials from 'request'. """
cookie = request.cookies.get('.ASPXAUTH') if cookie: creds = {} creds['cookie'] = cookie creds['plugin'] = self.getId()
return creds
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)
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
Plone Conference 2013 - Brasilia
security.declarePrivate( 'resetCredentials' ) def resetCredentials(self, request, response): """ Raise unauthorized to tell browser to clear credentials. """ response.expireCookie('.ASPXAUTH', path='/', domain='.netsightdev.co.uk')
Plone Conference 2013 - Brasilia
Gotchas
• UserId versus Login
• Plugin Performance
• Plugin Order
• Current paster/templar template missing (sprint?)
Plone Conference 2013 - Brasilia
Obrigado!Matt Hamilton
@hammertoe
http://slideshare.net/hammertoe