Skip to content

Commit

Permalink
feat(setup): Zope root cookie login form profile
Browse files Browse the repository at this point in the history
Move the change of the default Zope root configuration from HTTP Basic auth to the
cookie login form [into a separate GenericSetup upgrade step to make the change
optional](plone/plone.restapi#1304 (comment)).

This reverts commit 132c2c390801ff16393f214c1501252b240cb62a.
  • Loading branch information
rpatterson committed Feb 14, 2022
1 parent 955a276 commit beeb79d
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 20 deletions.
4 changes: 4 additions & 0 deletions news/zope-root-cookie.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add separate `GenericSetup` profile to switch the Zope root `/acl_users` to use a simple
cookie login form. Useful when Zope root login and logout need to synchronize
authentication state between multiple plugins, which is not possible with HTTP `Basic
...` authentication. [rpatterson] (#65)
16 changes: 16 additions & 0 deletions src/Products/PlonePAS/profiles.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,20 @@
<depends name="rolemap" />
</genericsetup:importStep>

<genericsetup:registerProfile
name="root-cookie"
title="Zope Root Cookie Login"
description="Change the Zope root `/acl_users` to use a simple cookie login form
instead of HTTP `Basic ...` for authentication."
provides="Products.GenericSetup.interfaces.EXTENSION"
/>
<genericsetup:importStep
name="zope-root-auth-cookie"
title="Zope Root Cookie Login"
description="Change the Zope root `/acl_users` to use a simple cookie login form
instead of HTTP `Basic ...` for authentication."
handler=".setuphandlers.set_up_zope_root_cookie_auth">
<depends name="plonepas" />
</genericsetup:importStep>

</configure>
4 changes: 4 additions & 0 deletions src/Products/PlonePAS/profiles/root-cookie/metadata.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<metadata>
<version>1</version>
</metadata>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Change the Zope root `/acl_users` to use a simple cookie login form instead of HTTP
`Basic ...` for authentication.
52 changes: 44 additions & 8 deletions src/Products/PlonePAS/setuphandlers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
"""
Custom GenericSetup import steps for PAS in Plone.
"""

from Acquisition import aq_base
from Acquisition import aq_parent
from Products.CMFCore.utils import getToolByName
Expand Down Expand Up @@ -324,14 +328,9 @@ def migrate_root_uf(self):
pas = uf.manage_addProduct['PluggableAuthService']
plone_pas = uf.manage_addProduct['PlonePAS']
# Setup authentication plugins
setupAuthPlugins(
parent,
pas,
plone_pas,
deactivate_basic_reset=False,
# Switch from HTTP `Authorization: Basic ...` to cookie login form
deactivate_cookie_challenge=False,
)
setupAuthPlugins(parent, pas, plone_pas,
deactivate_basic_reset=False,
deactivate_cookie_challenge=True)

# Activate *all* interfaces for user manager. IUserAdder is not
# activated for some reason by default.
Expand Down Expand Up @@ -518,3 +517,40 @@ def setupPlonePAS(context):
addRolesToPlugIn(site)
setupGroups(site)
setLoginFormInCookieAuth(site)


def set_up_zope_root_cookie_auth(context):
"""
Change the Zope root `/acl_users` to use a simple cookie login form.
"""
# Only run step if a flag file is present, IOW not for every profile
if context.readDataFile("plone-pas-zope-root-cookie.txt") is None:
return
portal = context.getSite()
root = portal.getPhysicalRoot()
root_acl_users = getToolByName(root, "acl_users")

# Enable the cookie plugin for all interfaces
activatePluginInterfaces(root, "credentials_cookie_auth")
# Ensure that the cookie login form is used to challenge for authentication
credentials_cookie_auth = root_acl_users._getOb( # pylint: disable=protected-access
"credentials_cookie_auth",
)
root_acl_users.plugins.movePluginsTop(
IChallengePlugin,
[credentials_cookie_auth.id],
)
# Disable the HTTP `Basic ...` authentication plugin
root_acl_users.plugins.deactivatePlugin(
IChallengePlugin,
'credentials_basic_auth'
)
# Apparently, the `HTTPBasicAuthHelper` plugin's `ICredentialsResetPlugin`
# implementation interferes with deleting/expiring cookies, specifically the `__ac`
# cookie in this case. I first tried moving that plugin to the top and bottom for
# that interface, but the cookies still remained after logout. Only deactivating
# it worked.
root_acl_users.plugins.deactivatePlugin(
ICredentialsResetPlugin,
'credentials_basic_auth'
)
24 changes: 12 additions & 12 deletions src/Products/PlonePAS/tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ def setUp(self):
"""
self.app = self.layer["app"]
self.root_acl_users = self.app.acl_users
self.portal = self.layer["portal"]

def test_zope_root_basic_challenge(self):
def test_zope_root_default_challenge(self):
"""
The Zope root `/acl_users` basic challenge plugin works.
The Zope root `/acl_users` default challenge plugin works.
"""
# Make the basic plugin the default auth challenge
# Check the Zope root PAS plugin configuration
self.assertIn(
"credentials_basic_auth",
self.root_acl_users.objectIds(),
Expand All @@ -46,11 +47,6 @@ def test_zope_root_basic_challenge(self):
HTTPBasicAuthHelper.HTTPBasicAuthHelper,
"Wrong Zope root `/acl_users` basic auth plugin type",
)
self.root_acl_users.plugins.movePluginsTop(
plugins_ifaces.IChallengePlugin,
[basic_plugin.id],
)
transaction.commit()
challenge_plugins = self.root_acl_users.plugins.listPlugins(
plugins_ifaces.IChallengePlugin,
)
Expand All @@ -68,14 +64,18 @@ def test_zope_root_basic_challenge(self):
self.assertEqual(
browser.headers["Status"].lower(),
"401 unauthorized",
"Wrong Zope root `/acl_users` basic challenge response status",
"Wrong Zope root `/acl_users` default challenge response status",
)

def test_zope_root_default_login(self):
def test_zope_root_cookie_login(self):
"""
The Zope root `/acl_users` default login works.
The Zope root `/acl_users` cookie login works.
"""
# Check the Zope root PAS plugin configuration
# Install the GenericSetup profile that performs the actual switch
pa_testing.applyProfile(self.portal, 'Products.PlonePAS:root-cookie')
transaction.commit()

# Make the cookie plugin the default auth challenge
self.assertIn(
"credentials_cookie_auth",
self.root_acl_users.objectIds(),
Expand Down

0 comments on commit beeb79d

Please sign in to comment.