Pas.plugin.ldap is case insensitive but Plone is not

Hi,
we are running Plone 5.2.0 using Python 3.6.8. We added the pas.plugin.ldap plugin to Plone and connected it to the active directory of our customer. Now we have the following scenario:

A user creates some content and uses the @@sharing view to give an other user John.Doe the rights to edit the content too.
If the user John.Doe now logs in he is able to edit that specific content. So far, so good.

But he also could login as john.doe or every other case sensitivity. pas.plugin.ldap seems to have no problem with that, but Plone does not recognize the user anymore and so he has no rights to edit the content anymore.

How can I configure the login logic so that it is not possible to login if the case sensitivity does not match with the user in the active directory? Or how can I make the login case insensitive but still use the correct user id stored in the active directory?

I hope my explanation was clear enough. Do you need any further information?

I can confirm this issue, I opened https://github.com/collective/pas.plugins.ldap/issues/104

I was already aware of that and added an extraction PAS plugin to quick fix it.
This is not the real code but should be a nice start if you want a quick solution while https://github.com/collective/pas.plugins.ldap/issues/104 get's fixed.

# coding=utf-8
from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass
from Products.PluggableAuthService.interfaces.plugins import IExtractionPlugin
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.PluggableAuthService.utils import classImplements


class UsernameFixerPlugin(BasePlugin):

    meta_type = "Login username case fixer Plugin"
    security = ClassSecurityInfo()

    def __init__(self, id, title=None):
        self._id = self.id = id
        self.title = title

    def _maybe_fix_username(self, request):
        if not request:
            return
        username = request.form.get("__ac_name")
        if not username:
            return
        request.form["__ac_name"] = username.lower()

    def extractCredentials(self, request):
        """ Does nothing except maybe fiddling with the request data
        """
        self._maybe_fix_username(request)
        return {}


InitializeClass(UsernameFixerPlugin)
classImplements(UsernameFixerPlugin, IExtractionPlugin)

Thank you! Sounds good.

Where should I put that code exactly to fix it? Can I paste it in the __init__.py of our own package or do I have to patch it somewhere in the original pas.plugins.ldap egg?

@alert knows for sure but I think if you add it to the __init__.py it will get executed on Zope startup and it registers itself as an extra plugin in the PAS ecosystem. Then it is run early in the authentication phase when all IExtraction PAS plugins are run. Maybe you can change the order as well in the ZMI (acl_users).

I haven't experienced this problem 'in the wild' yet, allthough we have a similar pas.plugins.ldap with many editors where one typo should have triggered this issue by now. Let's put the testing environment to some good use >;-}

You could also set the login_transform of PAS to lower and see if that helps. That is one of the things that is done when you select email as login. It might help for your case too:

http://localhost:8080/Plone/acl_users/manage_propertiesForm

See also the code in CMFPlone that sets this.

1 Like

Yes, @fredvd is right, you can put it in your package and the plugin should become available between the ones you can add in the ZMI.

I just tried the solution from @mauritsvanrees which seems to make the same thing as the code from @alert. Unfortunately this makes it even worse. And I now understand why this is the case.

If you are an administrator you can use the @@sharing view on a context to give other LDAP users certain roles. In that view you can search for a user and then you will see all matching users in a list. The listed names were extracted directly from the LDAP server and in my case they are mostly capitalized names, e.g. John.Doe. If the admin now gives John.Doe his new roles they will only work if John.Doe correctly logs into Plone with his name written like it is stored in the LDAP. If he tries to log in using john.doe or if I enable the lowercase functionality mentioned above he will never see his new roles but can still log in.

So instead of the code snippet from @alert I would need a snipped which transforms the login name to the exact name how it is stored in the LDAP server. Which means if John Doe logs in as john.doe it should be transformed to John.Doe.

>>> a = 'john.doe'
>>> '.'.join(list([b.capitalize() for b in a.split('.')]))
'John.Doe'

use this instead of lower()

It's not that simple. The login credentials are not consistent in the database. Most of them are capitalized, but there also names like username58 or Firstname.Long-Lastname and some other variations. I can not develop a simple function which always converts login names the right way. I think I need to modify the pas.plugin.ldap addon itself to make it work. But for this I first have to understand the plugin better, fork it and put it in our own egg proxy to get it to the production server I guess.

Note that my code snippet was just using lower as an example :slight_smile:
You can indeed add all the logic you want to get the proper case.

Yes, I understand I can process the name as I want. To make it work I need to be able to access the LDAP information to get the correct name as stored on the LDAP server, an then replace __ac_name accordingly. I have to figure out how this could be solved.

To make the solution easier to find for other people, here is the link to my own comment on a Github issue which solves the problem for me: https://github.com/collective/pas.plugins.ldap/issues/104#issuecomment-633968316

The bug is within the package node.ext.ldap and only occurs if id and login are mapped to the same LDAP attribute or if both fields contain the same value. The solution is as simple as removing a seemingly uneccessary condition of an if clause.

Thanks to @alert for digging into it.

1 Like

Solved via https://github.com/bluedynamics/node.ext.ldap/commit/579fee7a811b312bb0a9f643c2bb91bcdab2ee1b

1 Like