OpenID Users from Keycloak don't work with the zopyx.usersascontent addon?

Maybe you're logged in Keycloak but still not in Plone. You should do the event notification later, in another PAS plugin maybe?

1 Like

@yurj You're right, thanks for this tip, the callback view has not yet logged in the user within Plone, it has only logged in the user in Keycloak.

Here I show a very debugging session to detect as the proper context to access the member object
to invoke my user_logged_in_first event handler.

When I log in using the /login URL with the anapoleo user, The UserInitialLoginInEvent event is automatically fired,
because it is the first time that the user logs in:

2024-04-09 02:49:52,616 INFO    [collective.fingerpointing:75][waitress-1] user=anapoleo ip=127.0.0.1 action=login
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(73)user_logged_in_first()
     72     import ipdb ; ipdb.set_trace()
---> 73     portal = api.portal.get()
     74     request = getattr(portal, "REQUEST", None)

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(74)user_logged_in_first()
     73     portal = api.portal.get()
---> 74     request = getattr(portal, "REQUEST", None)
     75     user_folder_id = api.portal.get_registry_record(

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(76)user_logged_in_first()
     75     request = getattr(portal, "REQUEST", None)
---> 76     user_folder_id = api.portal.get_registry_record(
     77         "user_folder_id", ICommunitySettings

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(77)user_logged_in_first()
     76     user_folder_id = api.portal.get_registry_record(
---> 77         "user_folder_id", ICommunitySettings
     78     )

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(76)user_logged_in_first()
     75     request = getattr(portal, "REQUEST", None)
---> 76     user_folder_id = api.portal.get_registry_record(
     77         "user_folder_id", ICommunitySettings

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(80)user_logged_in_first()
     79
---> 80     if not ILayer.providedBy(request):
     81         return

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(83)user_logged_in_first()
     82
---> 83     with api.env.adopt_roles(roles=["Manager"]):
     84

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(86)user_logged_in_first()
     85         # Plone User Folder
---> 86         if user_folder_id not in portal.objectIds():
     87             uf = api.content.create(

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(95)user_logged_in_first()
     94         else:
---> 95             LOG.info(f"The Plone User Folder '{user_folder_id}' exists as a content type!")
     96         uf = portal[user_folder_id]

ipdb>
2024-04-09 02:50:11,251 INFO    [my.package:95][waitress-1] The Plone User Folder 'users' exists as a content type!
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(96)user_logged_in_first()
     95             LOG.info(f"The Plone User Folder '{user_folder_id}' exists as a content type!")
---> 96         uf = portal[user_folder_id]
     97

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(99)user_logged_in_first()
     98         # Plone User
---> 99         user = api.user.get_current()
    100         user_id = user.getId()

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(100)user_logged_in_first()
     99         user = api.user.get_current()
--> 100         user_id = user.getId()
    101         plone_user_obj = None

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(101)user_logged_in_first()
    100         user_id = user.getId()
--> 101         plone_user_obj = None
    102

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(103)user_logged_in_first()
    102
--> 103         if user_id not in uf.objectIds():
    104             try:

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(104)user_logged_in_first()
    103         if user_id not in uf.objectIds():
--> 104             try:
    105                 plone_user_obj = api.content.create(

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(105)user_logged_in_first()
    104             try:
--> 105                 plone_user_obj = api.content.create(
    106                     type="PloneUser",

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(106)user_logged_in_first()
    105                 plone_user_obj = api.content.create(
--> 106                     type="PloneUser",
    107                     id=user_id or "john.doe",

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(107)user_logged_in_first()
    106                     type="PloneUser",
--> 107                     id=user_id or "john.doe",
    108                     title=user_id or "John Doe",

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(108)user_logged_in_first()
    107                     id=user_id or "john.doe",
--> 108                     title=user_id or "John Doe",
    109                     container=uf,

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(109)user_logged_in_first()
    108                     title=user_id or "John Doe",
--> 109                     container=uf,
    110                     email=user.getProperty('email') or "john.doe@mail.com",

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(110)user_logged_in_first()
    109                     container=uf,
--> 110                     email=user.getProperty('email') or "john.doe@mail.com",
    111                     accept=user.getProperty('accept') or True,

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(111)user_logged_in_first()
    110                     email=user.getProperty('email') or "john.doe@mail.com",
--> 111                     accept=user.getProperty('accept') or True,
    112                     newsletter=user.getProperty('newsletter') or False

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(112)user_logged_in_first()
    111                     accept=user.getProperty('accept') or True,
--> 112                     newsletter=user.getProperty('newsletter') or False
    113                 )

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(105)user_logged_in_first()
    104             try:
--> 105                 plone_user_obj = api.content.create(
    106                     type="PloneUser",

ipdb>
2024-04-09 02:50:18,851 INFO    [collective.fingerpointing:75][waitress-1] user=anapoleo ip=127.0.0.1 action=create object=<PloneUser at /my/users/anapoleo>
2024-04-09 02:50:19,108 INFO    [collective.fingerpointing:75][waitress-1] user=anapoleo ip=127.0.0.1 action=modify object=<PloneUserFolder at /my/users>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(114)user_logged_in_first()
    113                 )
--> 114                 api.portal.show_message(
    115                     "Your account has been created.", request

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(115)user_logged_in_first()
    114                 api.portal.show_message(
--> 115                     "Your account has been created.", request
    116                 )

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(114)user_logged_in_first()
    113                 )
--> 114                 api.portal.show_message(
    115                     "Your account has been created.", request

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(117)user_logged_in_first()
    116                 )
--> 117                 LOG.info(f"The Plone User '{plone_user_obj.getId()}' was created as a content type!")
    118             except InvalidParameterError as err:

ipdb> n
2024-04-09 02:50:29,275 INFO    [my.package:117][waitress-1] The Plone User 'anapoleo' was created as a content type!
ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(134)user_logged_in_first()
    133
--> 134         import ipdb ; ipdb.set_trace()
    135         # Grant the role 'Member' to user logged in

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(136)user_logged_in_first()
    135         # Grant the role 'Member' to user logged in
--> 136         roles = api.user.get_roles(username=user_id)
    137         if not "Member" in roles:

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(137)user_logged_in_first()
    136         roles = api.user.get_roles(username=user_id)
--> 137         if not "Member" in roles:
    138             try:

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(156)user_logged_in_first()
    155         else:
--> 156             LOG.info(f"The '{user_id}' object has the role 'Member'!")
    157

ipdb>
2024-04-09 02:50:52,644 INFO    [my.package:156][waitress-1] The 'anapoleo' object has the role 'Member'!
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(158)user_logged_in_first()
    157
--> 158         import ipdb ; ipdb.set_trace()
    159         # User logged in the first time?

ipdb>
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(160)user_logged_in_first()
    159         # User logged in the first time?
--> 160         if user_id in uf.objectIds():
    161             if not plone_user_obj.first_login:

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(161)user_logged_in_first()
    160         if user_id in uf.objectIds():
--> 161             if not plone_user_obj.first_login:
    162                 plone_user_obj.first_login = datetime.now(timezone.utc)

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(162)user_logged_in_first()
    161             if not plone_user_obj.first_login:
--> 162                 plone_user_obj.first_login = datetime.now(timezone.utc)
    163                 plone_user_obj.last_login = datetime.now(timezone.utc)

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(163)user_logged_in_first()
    162                 plone_user_obj.first_login = datetime.now(timezone.utc)
--> 163                 plone_user_obj.last_login = datetime.now(timezone.utc)
    164                 LOG.info(f"The 'first_login' and 'last_login' properties of '{plone_user_obj.getId()}' object were updated!")

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(164)user_logged_in_first()
    163                 plone_user_obj.last_login = datetime.now(timezone.utc)
--> 164                 LOG.info(f"The 'first_login' and 'last_login' properties of '{plone_user_obj.getId()}' object were updated!")
    165

ipdb> n
2024-04-09 02:51:08,283 INFO    [my.package:164][waitress-1] The 'first_login' and 'last_login' properties of 'anapoleo' object were updated!
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(166)user_logged_in_first()
    165
--> 166                 redirect_after_registration = api.portal.get_registry_record(
    167                     "redirect_after_registration", ICommunitySettings

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(167)user_logged_in_first()
    166                 redirect_after_registration = api.portal.get_registry_record(
--> 167                     "redirect_after_registration", ICommunitySettings
    168                 )

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(166)user_logged_in_first()
    165
--> 166                 redirect_after_registration = api.portal.get_registry_record(
    167                     "redirect_after_registration", ICommunitySettings

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(169)user_logged_in_first()
    168                 )
--> 169                 if redirect_after_registration:
    170                     return plone_user_obj.absolute_url()

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(170)user_logged_in_first()
    169                 if redirect_after_registration:
--> 170                     return plone_user_obj.absolute_url()
    171

ipdb> n
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(83)user_logged_in_first()
     82
---> 83     with api.env.adopt_roles(roles=["Manager"]):
     84

ipdb> n
--Return--
'http://local...sers/anapoleo'
> /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/handlers.py(83)user_logged_in_first()
     82
---> 83     with api.env.adopt_roles(roles=["Manager"]):
     84

Next, continue the user authentication workflow within Plone:

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.interface-6.0-py3.11-linux-x86_64.egg/zope/interface/adapter.py(895)subscribers()
    894             result = ()
--> 895             for subscription in subscriptions:
    896                 subscription(*objects)

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.interface-6.0-py3.11-linux-x86_64.egg/zope/interface/adapter.py(903)subscribers()
    902                     result.append(subscriber)
--> 903         return result
    904

ipdb>
--Return--
()
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.interface-6.0-py3.11-linux-x86_64.egg/zope/interface/adapter.py(903)subscribers()
    902                     result.append(subscriber)
--> 903         return result
    904

ipdb>
--Return--
()
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.interface-6.0-py3.11-linux-x86_64.egg/zope/interface/registry.py(445)subscribers()
    444     def subscribers(self, objects, provided):
--> 445         return self.adapters.subscribers(objects, provided)
    446

ipdb>
--Return--
()
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.component-6.0-py3.11.egg/zope/component/_api.py(146)subscribers()
    145         return []
--> 146     return sitemanager.subscribers(objects, interface)
    147

ipdb>
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.component-6.0-py3.11.egg/zope/component/event.py(27)dispatch()
     26 def dispatch(*event):
---> 27     component_subscribers(event, None)
     28

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.event-5.0-py3.11.egg/zope/event/__init__.py(32)notify()
     31     """
---> 32     for subscriber in subscribers:
     33         subscriber(event)

ipdb>
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/zope.event-5.0-py3.11.egg/zope/event/__init__.py(32)notify()
     31     """
---> 32     for subscriber in subscribers:
     33         subscriber(event)

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(662)loginUser()
    661
--> 662         if REQUEST is None:
    663             REQUEST = getattr(self, "REQUEST", None)

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(664)loginUser()
    663             REQUEST = getattr(self, "REQUEST", None)
--> 664         if REQUEST is None:
    665             return

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(668)loginUser()
    667         # Expire the clipboard
--> 668         if REQUEST.get("__cp", None) is not None:
    669             REQUEST.RESPONSE.expireCookie("__cp", path="/")

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(671)loginUser()
    670
--> 671         self.createMemberArea()
    672

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(673)loginUser()
    672
--> 673         try:
    674             pas = getToolByName(self, "acl_users")

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(674)loginUser()
    673         try:
--> 674             pas = getToolByName(self, "acl_users")
    675             pas.credentials_cookie_auth.login()

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(675)loginUser()
    674             pas = getToolByName(self, "acl_users")
--> 675             pas.credentials_cookie_auth.login()
    676         except AttributeError:

ipdb> locals()
{'self': <MembershipTool at /Plone/portal_membership>, 'REQUEST': <WSGIRequest, URL=http://localhost:7080/my/login>, 'user': <PloneUser 'anapoleo'>, 'pas': <PluggableAuthService at /my/acl_users>}
ipdb> user.__dir__()
['_id', '_login', '_propertysheets', '_groups', '_roles', '__module__', '_isGroup', '__init__', '_getPAS', '_getPlugins', 
'isGroup', 'getName', 'getUserId', 'getGroupNames', 'getGroupIds', 'getPropertysheet', 'addPropertysheet', '_getPropertyPlugins', 
'getOrderedPropertySheets', '_getLocalRolesPlugins', 'getRolesInContext', 'allowed', 'setProperties', 'getProperty', 'isGroup__roles__', 
'getName__roles__', 'getUserId__roles__', 'getGroupNames__roles__', 'getGroupIds__roles__', 'getPropertysheet__roles__', 
'addPropertysheet__roles__', 'getOrderedPropertySheets__roles__', '__ac_permissions__', '__implemented__', '__provides__', '__doc__', 
'getId', 'getUserName', 'getRoles', 'getGroups', 'getDomains', '_addGroups', '_addRoles', 'listPropertysheets', '__getitem__', 
'zmi_icon', '__allow_access_to_unprotected_subobjects__', '_getPassword', 'authenticate', '_check_context', 'domains', 'has_role', 
'has_permission', '__len__', '__str__', '__repr__', '__dict__', '__weakref__', '__providedBy__', '__new__', '__of__', '__getattribute__', 
'__getstate__', '__setstate__', '__reduce__', '__hash__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', 
'__ge__', '__reduce_ex__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
ipdb> user.getId()
'anapoleo'
ipdb> user.getUserName()
'anapoleo'
ipdb> n
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.PlonePAS-8.0.2-py3.11.egg/Products/PlonePAS/tools/membership.py(675)loginUser()
    674             pas = getToolByName(self, "acl_users")
--> 675             pas.credentials_cookie_auth.login()
    676         except AttributeError:

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(144)_post_login()
    143         membership_tool.loginUser(self.request)
--> 144         if is_initial_login:
    145             self.handle_initial_login()

ipdb> locals()
{'self': <Products.Five.browser.metaconfigure.SimpleViewClass from /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/browser/login/templates/login.pt object at 0x7f90ef78c4d0>, 'membership_tool': <MembershipTool at /Plone/portal_membership>, 'member': <Products.PlonePAS.tools.memberdata.MemberData object at 0x7f90efb8a750>, 'must_change_password': 0, 'login_time': DateTime('2000/01/01 00:00:00 GMT-4'), 'is_initial_login': True}
ipdb> member.__dir__()
['_user', '_tool', '__parent__', '_md', 'id', '__module__', '__init__', 'setMemberProperties', 'getProperty', 'hasProperty',
'getPassword', 'canDelete', 'canPasswordSet', 'passwordInClear', '_memberdataHasProperty', 'canWriteProperty', 'canAddToGroup',
'canRemoveFromGroup', 'canAssignRole', 'setSecurityProfile', '_getPlugins', 'has_permission', 'getGroups', 'getUserId', '__doc__',
'__component_adapts__', '__implemented__', '__provides__', 'hasProperty__roles__', 'setSecurityProfile__roles__', '_getPlugins__roles__',
'has_permission__roles__', 'getGroups__roles__', 'getUserId__roles__', '__ac_permissions__', 'notifyModified', 'getUser', 'getMemberId', 
'setProperties', '__str__', 'getId', 'getUserName', 'getRoles', 'getRolesInContext', 'getDomains', 'has_role', '__dict__', '__weakref__', 
'__providedBy__', 'notifyModified__roles__', 'getUser__roles__', 'getMemberId__roles__', 'setMemberProperties__roles__', 
'getProperty__roles__', 'getPassword__roles__', 'getId__roles__', 'getUserName__roles__', 'getRoles__roles__', 'getRolesInContext__roles__', 
'getDomains__roles__', 'has_role__roles__', 'setProperties__roles__', '__new__', '__repr__', '__hash__', '__getattribute__', '__setattr__', 
'__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', 
'__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
ipdb> member.getId()
'anapoleo'
ipdb> member.getUserName()
'anapoleo'
ipdb> member.getRoles()
['Member', 'Authenticated']
ipdb> member.hasProperty('email')
True

In the _post_login() method into LoginForm class from the Products.CMFPlone.browser.login.login.py module, here the right
context to access all the member data needs to fire my event handler with the right data session because here the Keycloak user is logged
in into Plone.

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(145)_post_login()
    144         if is_initial_login:
--> 145             self.handle_initial_login()
    146

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(147)_post_login()
    146
--> 147         if must_change_password:
    148             self.force_password_change()

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(149)_post_login()
    148             self.force_password_change()
--> 149         return is_initial_login
    150

ipdb>
--Return--
True
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(149)_post_login()
    148             self.force_password_change()
--> 149         return is_initial_login
    150

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(180)handleLogin()
    179         is_initial_login = self._post_login()
--> 180         status_msg.addStatusMessage(
    181             _(

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(181)handleLogin()
    180         status_msg.addStatusMessage(
--> 181             _(
    182                 "you_are_now_logged_in",

ipdb> locals()
{'self': <Products.Five.browser.metaconfigure.SimpleViewClass from /home/macagua/projects/plone-6.0.7/src/my.package/src/my/package/browser/login/templates/login.pt object at 0x7f90ef78c4d0>,
'action': <ButtonAction 'buttons.login' 'Log in'>, 'data': {'ac_name': 'anapoleo', 'ac_password': 'anapoleo123', 'came_from': 'http://localhost:7080/Plone'}, 'errors': (), 'membership_tool': <MembershipTool at /Plone/portal_membership>, 'status_msg': <Products.statusmessages.adapter.StatusMessage object at 0x7f90ef34e310>, 'is_initial_login': True}
ipdb> data['ac_name']
'anapoleo'
ipdb> data['ac_password']
'anapoleo123'
ipdb> membership_tool.getAuthenticatedMember()
<Products.PlonePAS.tools.memberdata.MemberData object at 0x7f90ee7db710>
ipdb> authenticated_member = membership_tool.getAuthenticatedMember()
ipdb> authenticated_member.getUserName()
'anapoleo'
ipdb> authenticated_member.getUser()
<PloneUser 'anapoleo'>
ipdb> authenticated_member.getId()
'anapoleo'
ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(182)handleLogin()
    181             _(
--> 182                 "you_are_now_logged_in",
    183                 default="Welcome! You are now logged in.",

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(183)handleLogin()
    182                 "you_are_now_logged_in",
--> 183                 default="Welcome! You are now logged in.",
    184             ),

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(181)handleLogin()
    180         status_msg.addStatusMessage(
--> 181             _(
    182                 "you_are_now_logged_in",

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(185)handleLogin()
    184             ),
--> 185             "info",
    186         )

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(180)handleLogin()
    179         is_initial_login = self._post_login()
--> 180         status_msg.addStatusMessage(
    181             _(

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(188)handleLogin()
    187
--> 188         came_from = data.get("came_from", None)
    189         self.redirect_after_login(came_from, is_initial_login)

ipdb>
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(189)handleLogin()
    188         came_from = data.get("came_from", None)
--> 189         self.redirect_after_login(came_from, is_initial_login)
    190

ipdb>
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/Products.CMFPlone-6.0.7-py3.11.egg/Products/CMFPlone/browser/login/login.py(189)handleLogin()
    188         came_from = data.get("came_from", None)
--> 189         self.redirect_after_login(came_from, is_initial_login)
    190

ipdb>
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/z3c.form-4.3-py3.11.egg/z3c/form/button.py(159)__call__()
    158     def __call__(self, form, action):
--> 159         return self.func(form, action)
    160

ipdb> n
--Return--
None
> /home/macagua/projects/plone-6.0.7/eggs/cp311/z3c.form-4.3-py3.11.egg/z3c/form/button.py(301)__call__()
    299         if handler is None:
    300             return
--> 301         return handler(self.form, self.action)

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/z3c.form-4.3-py3.11.egg/z3c/form/action.py(102)execute()
    101                 else:
--> 102                     zope.event.notify(ActionSuccessful(action))
    103                     return result

ipdb> n
> /home/macagua/projects/plone-6.0.7/eggs/cp311/z3c.form-4.3-py3.11.egg/z3c/form/action.py(103)execute()
    102                     zope.event.notify(ActionSuccessful(action))
--> 103                     return result
    104

ipdb> c
2024-04-09 03:05:30,017 INFO    [collective.fingerpointing:75][waitress-0] user=anapoleo ip=127.0.0.1 action=modify object=<PloneUser at /my/users/anapoleo>
2024-04-09 03:05:34,592 INFO    [collective.fingerpointing:75][waitress-1] user=anapoleo ip=127.0.0.1 action=logout

My question is: is there possible to override the _post_login() method into LoginForm class from the
Products.CMFPlone.browser.login.login.py module?

I added a new my/package/browser/login.py file with the following content:

from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.browser.login.login import LoginForm as BaseLoginForm
from Products.CMFPlone.browser.login.utils import has_logged_in
from Products.PlonePAS.events import UserInitialLoginInEvent
from zope.event import notify
from my.package.logger import LOG


class CustomLoginForm(BaseLoginForm):
    """An Override Implementation of the login form class

    Args:
        LoginForm (cls): Login Form Class
    """    

    def _post_login(self):
        membership_tool = getToolByName(self.context, "portal_membership")
        member = membership_tool.getAuthenticatedMember()
        must_change_password = member.getProperty("must_change_password", 0)
        login_time = member.getProperty("login_time", None)
        is_initial_login = not has_logged_in(login_time)

        membership_tool.loginUser(self.request)
        if is_initial_login:
            try:
                # Create the event instance
                event = UserInitialLoginInEvent(member)
                # Notify the system
                notify(event)
            except Exception as e:
                LOG.error(e)
            self.handle_initial_login()

        if must_change_password:
            self.force_password_change()
        return is_initial_login

    def __call__(self):
        """custom __call__ method"""
        import ipdb ; ipdb.set_trace()
        self._post_login()
        try:
            return super().__call__()
        except Exception as e:
            LOG.info("There was an error handling the login process")
            LOG.error(e)
            self.request.response.setHeader(
                "Cache-Control", "no-cache, must-revalidate"
            )
            return self.request.response.redirect("/")

I added into the my/package/browser/configure.zcml file the new entry:

  <!-- Login Form -->
  <browser:page
      for="plone.base.interfaces.ILoginForm"
      name="login"
      class="my.package.browser.login.CustomLoginForm"
      layer="my.package.interfaces.ILayer"
      permission="zope2.View"
      />

Next stop and start the Zope instance, reinstall my.package addon, and try to log in from the
/acl_users/oidc/login URL with an External user from Keycloak, the CustomLoginForm class is omitted
because my breakpoint into __callback__ method is not fired.

How override the _post_login method with my code?

I think you want to use override.zcml. But you can also use z3c.jbot to override things in your package. My question is how PAS knows about CMFPlone/browser/login/login.py? Maybe you can just add a plugin to manage the login as Plone does? I'm not a PAS expert so I don't really know.

1 Like

UPDATED:

The SOLUTION was done with:

These improvements were released on the pas.plugins.oidc v2.0.0b1 - PyPI thank to @ericof

@yurj @mtrebron @espenmn @dieter @erral @zopyx thanks for your ideas and advice to help to fix that issue.

2 Likes