Description
Login and logout related programming activities in Plone
There are two login points in Plone
/login view (appended to any content URL) directs you to the page where you came from after the login.
/login_form view does login without the redirect back to the original page.
Extracting credentials try to extract log-in (username, password) from HTTP request.
Below is an example how to extract and authenticate the user manually. It is mostly sui le for unit testing. Note that given login field isn’t necessarily the username. For example, betahaus.emaillogin add-on authenticates users by their email addresses.
Credential extraction will go through all plug-ins registered for PlonePAS system.
The first found login/password pair attempt will be used for user authentication.
Unit test example:
def extract_credentials(self, login, password):
""" Spoof HTTP login attempt.
Functional test using zope.testbrowser would be
more appropriate way to test this.
"""
request = self.portal.REQUEST
# Assume publishing process has succeeded and object has been found by traversing
# (this is usually set by ZPublisher)
request['PUBLISHED'] = self.portal
# More ugly ZPublisher stubs
request['PARENTS'] = [self.portal]
request.steps = [self.portal]
# Spoof HTTP request login fields
request["__ac_name"] = login
request["__ac_password"] = password
# Call PluggableAuthService._extractUserIds()
# which will return a list of user ids extracted from the request
plugins = self.portal.acl_users.plugins
users = self.portal.acl_users._extractUserIds(request, plugins)
if len(users) == 0:
return None
self.assertEqual(len(users), 1)
# User will be none if the authentication fails
# or anonymous if there were no credential fields in HTTP request
return users[0]
Authenticating the user will check that username and password are correct.
Pluggable Authentication Service (acl_users under site root) will go through all authentication plug-ins and return the first succesful authenticated users.
Read more in PlonePAS.
Unit test example:
def authenticate_using_credentials(self, login, password):
request = self.portal.REQUEST
# Will return valid user object
user = self.portal.acl_users.authenticate(login, password, request)
self.assertNotEqual(user, None)
Post-login actions are executed after a successful login. Post-login actions which you could want to change are
#ANTTI:./eggs/Plone-3.2.3-py2.4.egg/Products/CMFPlone/skins/plone_login/logged_in.cpy.metadata #ANTTI:./eggs/Plone-3.2.3-py2.4.egg/Products/CMFPlone/skins/plone_login/logged_in.cpy
Post-login code is defined in CMFPlone/skins/plone_scripts/logged_in.cpy.
You need make a copy of both logged_in.cpy and logged_in.cpy.metadata to your add-on product skins structure to override these.
Example logged_in.cpy:
## Controller Python Script "logged_in"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind state=state
##bind subpath=traverse_subpath
##parameters=
##title=Initial post-login actions
##
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone import PloneMessageFactory as _
REQUEST=context.REQUEST
membership_tool=getToolByName(context, 'portal_membership')
if membership_tool.isAnonymousUser():
REQUEST.RESPONSE.expireCookie('__ac', path='/')
context.plone_utils.addPortalMessage(_(u'Login failed. Both login name and password are case sensitive, check that caps lock is not enabled.'), 'error')
return state.set(status='failure')
member = membership_tool.getAuthenticatedMember()
login_time = member.getProperty('login_time', '2000/01/01')
initial_login = int(str(login_time) == '2000/01/01')
state.set(initial_login=initial_login)
must_change_password = member.getProperty('must_change_password', 0)
state.set(must_change_password=must_change_password)
if initial_login:
state.set(status='initial_login')
elif must_change_password:
state.set(status='change_password')
membership_tool.loginUser(REQUEST)
#
# Special login code specific login code
#
# Debug log output about the user we are dealing with
context.plone_log("Got member:" + str(member))
# Check that if the user has a custom method which marks our special members
# needing special actions
if hasattr(member, "getLoginRedirect"):
# Show a custom login message
context.plone_utils.addPortalMessage(_(u'You are now logged in. Welcome to supa-dupa-system.'), 'info') # This message is in Plone i18n domain
# Go to a custom page after login
REQUEST.RESPONSE.redirect(context.portal_url() + "/some_folder")
return state