How can I expose my Plone session data to my Zope website?

Environment

  • Environment type: Standalone Plone instance on a VMware Player VM
  • Plone version: 5.2.2
  • Python version: 3.8.3
  • Guest OS: CentOS 8 Stream
  • VM hardware: 8 cores, 8 GB RAM, 5.8 GB free space
  • Physical OS: Debian 10 Buster, AMD64
  • Physical machine build: AMD FX 8350, 32 GB RAM, Nvidia Geforce GTX 1070, Asus M5A99X EVO R2.0 motherboard, 1 TB SSD

I am trying to expose my Plone session data to a web page I have loaded into my Zope instance. My goal is to be able to tell from the page I loaded on Zope if my user has logged into Plone, and be able to access my user information from that web page.

For good measure, I also have successfully integrated an ApacheDS LDAP server into both my Zope instance and my Plone site, by adding pas.plugins.ldap on buildout.cfg in the eggs and zcml parts, running Buildout, enabling the addon on my Plone website configuration, adding an LDAP Plugin on /acl_users in my Zope instance, and entering the same configuration to connect to my LDAP server on the LDAP plugin and Plone's "LDAP/AD support" control panel. Both my Plone site and my Zope instance can successfully see the same users I registered on both my LDAP server.

So, after piecing together many different loose documentation pages, the official readme for plone.session, a Stack Overflow message that explains some details about plone.session for use against other web servers and several other resources, I have gathered that I'm apparently supposed to use the plone.session add-on. I added it to the eggs and zcml parts of my buildout.cfg, ran Buildout, went to my Zope backend, navigated to /acl_users, and added a Plone Session Plugin.

However, when I try to open my Plone session plugin's configuration page, I get this error:

We’re sorry, but there seems to be an error…
ComponentLookupError
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 162, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 359, in publish_module
  Module ZPublisher.WSGIPublisher, line 254, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 63, in call_object
  Module Shared.DC.Scripts.Bindings, line 335, in __call__
  Module Shared.DC.Scripts.Bindings, line 372, in _bindAndExec
  Module Products.PageTemplates.PageTemplateFile, line 145, in _exec
  Module Products.PageTemplates.PageTemplate, line 83, in pt_render
  Module zope.pagetemplate.pagetemplate, line 133, in pt_render
  Module Products.PageTemplates.engine, line 367, in __call__
  Module z3c.pt.pagetemplate, line 176, in render
  Module chameleon.zpt.template, line 307, in render
  Module chameleon.template, line 214, in render
  Module chameleon.utils, line 75, in raise_with_traceback
  Module chameleon.template, line 192, in render
  Module 6fe10e1a75d4c66706c7b810b966b53b, line 389, in render
  Module zope.tales.expressions, line 250, in __call__
  Module Products.PageTemplates.Expressions, line 196, in _eval
  Module Products.PageTemplates.Expressions, line 126, in render
  Module plone.protect.authenticator, line 115, in authenticator
  Module plone.protect.authenticator, line 99, in createToken
  Module plone.protect.authenticator, line 51, in _getKeyring
  Module zope.component._api, line 165, in getUtility
zope.interface.interfaces.ComponentLookupError: zope.interface.interfaces.ComponentLookupError: (<InterfaceClass plone.keyring.interfaces.IKeyManager>, '')

 - Expression: "context/@@authenticator/authenticator"
 - Filename:   manage_secret
 - Location:   (line 50: col 34)
 - Arguments:  template: <PageTemplateFile at /acl_users/session_plone/secret>
               here: <SessionPlugin at /acl_users/session_plone>
               context: <SessionPlugin at /acl_users/session_plone>
               container: <SessionPlugin at /acl_users/session_plone>
               nothing: None
               options: {'args': ()}
               root: <Application at >
               request: <WSGIRequest, URL=http://localhost:39080/acl_users/session_plone/manage_secret>
               modules: <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter object at 0x7f9a0abe15e0>
               user: <PropertiedUser 'admin'>
               default: <DEFAULT>
               repeat: <Products.PageTemplates.engine.RepeatDictWrapper object at 0x7f99f4152700>
               loop: {}
               target_language: None
               translate: <function BaseTemplate.render.<locals>.translate at 0x7f99f4479a60>
               attrs: {}

I am honestly at a loss as to how to proceed. Am I missing something? Am I supposed to use another mechanism? Am I supposed to use a separate web server to handle authentication?

I try to repeat the problem:
You want to create a page (in Plone called a "view") and protect it in a way, so only the user itself (and admins) can see it, showing the users own data, others shall get an Unauthorized error?

What you did:

  • Successfully installed pas.plugins.ldap.
  • Not that succesfully installed plone.session

First: You do not need plone.session. This core-addon is used to create more secure cookies and logout inactive users. It is about authentication. It does not address Authorization nor user-data specific topics. Anyway, plone.session must work and if there is an issue it has to be fixed: A bug report would be great!

Now, back to the topic.

You need to utilize the access control system

  • Base is Zopes module AccessControl.
  • there are lots of permissions defined for many cases.
  • permissions are bundled into roles.
  • roles can be assigned to users and groups:
    • global: everywhere in the sites
    • local: only valid for a specific subtree in the hierarchical database
  • groups are a list of users. if a group has a role, every user has this role too.

So, knowing this, how does one protect a view?

  • you need to know which permission is relevant. Usually there are a bunch of common permissions, but your case use a not that often used: `
  • in ZCML you set the permission like so:
     <browser:page
      ...
      permission="PERMISSIONNAMEHERE"
      ...
      />
    

You need to know which permission is relevant. Usually there are a bunch of common permission used in a CMS plus some more specific ones.
Probably best matching is "CMF: Set own properties" - but this does not filter out other users data if requested. So this part need to be handled in your view code. Therefore plone.api has excellent functions available: ownuser = plone.api.user.get_current() return the current logged in user object.
With i.e. ownuser.getProperty('fullname') then the userdata (i.e.the data mapped in the LDAP plugin) can be read from the user object.

Further reading:

1 Like

OK, so what I'm gathering is that I should instead set my page on Zope's manager to have read permission only for logged in users. However, how can I make it so that logging into Plone will register on my Zope page as being logged in?

I'm going to test some of the stuff mentioned here in a moment, and will come back with results.

So I did a few more tries, and here's what I've done:

I set up an user checking script on my page folder, called siteauthentifier.py, with the following content:

import plone

request = container.REQUEST
response =  request.response

return plone.api.user.get_current()

Then I tried to load it from my page using TAL, as follows (doing it on <title> for simplicity):

<html>
<head>
   <title>
      <span tal:omit-tag="" tal:replace="container/siteauthentifier.py" />
   </title>
</head>
[...]
</html>

I get the following error:

We’re sorry, but there seems to be an error…
Unauthorized
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 162, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 359, in publish_module
  Module ZPublisher.WSGIPublisher, line 254, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 63, in call_object
  Module Shared.DC.Scripts.Bindings, line 335, in __call__
  Module Shared.DC.Scripts.Bindings, line 372, in _bindAndExec
  Module Products.PythonScripts.PythonScript, line 351, in _exec
  Module script, line 20, in autentificador.py
   - <PythonScript at /template/siteauthentifier.py>
   - Line 20
AccessControl.unauthorized.Unauthorized: You are not allowed to access 'user' in this context

Having read the previous links, my best guess is that maybe my code is running on RestrictedPython mode, or maybe it is simply not the way to go.

I then tried an alternative method by creating an addon with Plonecli and adding a simple browser view that outputs the username. I then installed the addon on Plone. The browser view is as follows:

# -*- coding: utf-8 -*-

from Products.Five.browser import BrowserView
from zope.component import getMultiAdapter

class UserName(BrowserView):
    def __init__(self, context, request):
       self.context = context
       self.request = request

    def __call__(self):
       portal_state = getMultiAdapter((self.context, self.request), name="plone_portal_state")

       if portal_state.anonymous():
          result = ""
       else:
          result = portal_state.member().getProperty("login")

       return result

However, it doesnt seem like my session data is making it to the browser view this way, because when I load that browser view with <span tal:omit-tag="" tal:replace="root/site/user-name" />, I just get an empty result, i.e. the browser view is receiving an anonymous session.

I'm not even sure if this is the way to go, but hopefully this can clarify why I intend to "transplant" my session data from Plone to Zope.