Commit bbd0f245 authored by Julien Muchembled's avatar Julien Muchembled

Reimplement ERP5Site_getAuthenticatedMemberPersonValue without using acl_users.erp5_users

Because some sites don't have an 'erp5_users' plugin in acl_users, and what
ERP5Site_getAuthenticatedMemberPersonValue does is not specific to 'erp5_users'.

This is done by moving code outside ERP5UserManager class so that it can be
reused. Changes to ERP5UserManager.getUserLogin method are:
- use a transactional cache instead of erp5_content_short:
  - a transactional cache is enough because authenticateCredentials already
    caches its result in erp5_content_short
  - no need to care about empty result from the catalog, which was done using
    _AuthenticationFailure (instead, we simply return an empty list)
  - cache person objects instead of their path
- no need to use SUPER_USER since we use unrestrictedSearchResults

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@37065 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent b9407de5
......@@ -57,11 +57,10 @@
"""\n
owner_id_list = [i[0] for i in context.get_local_roles() if \'Owner\' in i[1]]\n
if owner_id_list:\n
found_user_list = context.acl_users.erp5_users.getUserByLogin(owner_id_list[0])\n
from Products.ERP5Security.ERP5UserManager import getUserByLogin\n
found_user_list = getUserByLogin(context.getPortalObject(), tuple(owner_id_list))\n
if found_user_list:\n
return found_user_list[0].getTitle()\n
\n
return owner\n
</string> </value>
</item>
<item>
......@@ -106,8 +105,10 @@ return owner\n
<string>i</string>
<string>_getitem_</string>
<string>owner_id_list</string>
<string>Products.ERP5Security.ERP5UserManager</string>
<string>getUserByLogin</string>
<string>tuple</string>
<string>found_user_list</string>
<string>owner</string>
</tuple>
</value>
</item>
......
......@@ -56,15 +56,14 @@
<value> <string>"""Find and returns Person object for current logged in user.\n
Returns None if no corresponding person, for example when not using ERP5Security.ERP5UserManager.\n
"""\n
portal = context.getPortalObject()\n
if user_name is None:\n
user_name = context.portal_membership.getAuthenticatedMember()\n
user_name = portal.portal_membership.getAuthenticatedMember()\n
\n
found_user_list = context.acl_users.erp5_users.getUserByLogin(str(user_name))\n
found_users = len(found_user_list)\n
if found_users != 1:\n
return None\n
\n
return found_user_list[0]\n
from Products.ERP5Security.ERP5UserManager import getUserByLogin\n
found_user_list = getUserByLogin(portal, str(user_name))\n
if len(found_user_list) == 1:\n
return found_user_list[0]\n
</string> </value>
</item>
<item>
......@@ -110,13 +109,15 @@ return found_user_list[0]\n
<value>
<tuple>
<string>user_name</string>
<string>None</string>
<string>_getattr_</string>
<string>context</string>
<string>portal</string>
<string>None</string>
<string>Products.ERP5Security.ERP5UserManager</string>
<string>getUserByLogin</string>
<string>str</string>
<string>found_user_list</string>
<string>len</string>
<string>found_users</string>
<string>_getitem_</string>
</tuple>
</value>
......
803
\ No newline at end of file
804
\ No newline at end of file
......@@ -26,7 +26,7 @@ from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin
from Products.PluggableAuthService.interfaces.plugins import IUserEnumerationPlugin
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.Cache import CachingMethod, transactional_cached
from ZODB.POSException import ConflictError
import sys
from DateTime import DateTime
......@@ -64,6 +64,36 @@ class _AuthenticationFailure(Exception):
etc...)
"""
@transactional_cached(lambda portal, *args: args)
def getUserByLogin(portal, login, exact_match=True):
if isinstance(login, basestring):
login = login,
if exact_match:
reference_key = 'ExactMatch'
else:
reference_key = 'Keyword'
result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference',
portal_type="Person",
reference=dict(query=login, key=reference_key))
# XXX: Here, we filter catalog result list ALTHOUGH we did pass
# parameters to unrestrictedSearchResults to restrict result set.
# This is done because the following values can match person with
# reference "foo":
# "foo " because of MySQL (feature, PADSPACE collation):
# mysql> SELECT reference as r FROM catalog
# -> WHERE reference="foo ";
# +-----+
# | r |
# +-----+
# | foo |
# +-----+
# 1 row in set (0.01 sec)
# "bar OR foo" because of ZSQLCatalog tokenizing searched strings
# by default (feature).
return [x.getObject() for x in result if not exact_match
or x['reference'] in login]
class ERP5UserManager(BasePlugin):
""" PAS plugin for managing users in ERP5
......@@ -180,81 +210,30 @@ class ERP5UserManager(BasePlugin):
return tuple(user_info)
def getUserByLogin(self, login, exact_match=True):
# Search the Catalog for login and return a list of person objects
# login can be a string or a list of strings
# (no docstring to prevent publishing)
if not login:
return []
portal = self.getPortalObject()
def _getUserByLogin(login, exact_match):
# because we aren't logged in, we have to create our own
# SecurityManager to be able to access the Catalog
if isinstance(login, list):
login = tuple(login)
elif not isinstance(login, tuple):
login = (str(login),)
sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER:
newSecurityManager(self, self.getUser(SUPER_USER))
try:
try:
if exact_match:
reference_key = 'ExactMatch'
else:
reference_key = 'Keyword'
result = portal.portal_catalog.unrestrictedSearchResults(
select_expression='reference',
portal_type="Person",
reference=dict(query=login,
key=reference_key))
except ConflictError:
raise
except:
LOG('ERP5Security', PROBLEM, 'getUserByLogin failed', error=sys.exc_info())
# Here we must raise an exception to prevent callers from caching
# a result of a degraded situation.
# The kind of exception does not matter as long as it's catched by
# PAS and causes a lookup using another plugin or user folder.
# As PAS does not define explicitely such exception, we must use
# the _SWALLOWABLE_PLUGIN_EXCEPTIONS list.
raise _SWALLOWABLE_PLUGIN_EXCEPTIONS[0]
finally:
setSecurityManager(sm)
# XXX: Here, we filter catalog result list ALTHOUGH we did pass
# parameters to unrestrictedSearchResults to restrict result set.
# This is done because the following values can match person with
# reference "foo":
# "foo " because of MySQL (feature, PADSPACE collation):
# mysql> SELECT reference as r FROM catalog
# -> WHERE reference="foo ";
# +-----+
# | r |
# +-----+
# | foo |
# +-----+
# 1 row in set (0.01 sec)
# "bar OR foo" because of ZSQLCatalog tokenizing searched strings
# by default (feature).
result = [x.path for x in result if (not exact_match)
or x['reference'] in login]
if not result:
raise _AuthenticationFailure()
return result
_getUserByLogin = CachingMethod(_getUserByLogin,
id='ERP5UserManager_getUserByLogin',
cache_factory='erp5_content_short')
if isinstance(login, list):
login = tuple(login)
elif not isinstance(login, tuple):
login = str(login)
try:
return [portal.unrestrictedTraverse(x) for x in
_getUserByLogin(login, exact_match)]
except _AuthenticationFailure:
return []
return getUserByLogin(self.getPortalObject(), login, exact_match)
except ConflictError:
raise
except:
LOG('ERP5Security', PROBLEM, 'getUserByLogin failed', error=sys.exc_info())
# Here we must raise an exception to prevent callers from caching
# a result of a degraded situation.
# The kind of exception does not matter as long as it's catched by
# PAS and causes a lookup using another plugin or user folder.
# As PAS does not define explicitely such exception, we must use
# the _SWALLOWABLE_PLUGIN_EXCEPTIONS list.
raise _SWALLOWABLE_PLUGIN_EXCEPTIONS[0]
classImplements( ERP5UserManager
, IAuthenticationPlugin
......
......@@ -108,3 +108,6 @@ def initialize(context):
, icon='www/portal.gif'
)
from AccessControl.SecurityInfo import ModuleSecurityInfo
ModuleSecurityInfo('Products.ERP5Security.ERP5UserManager').declarePublic(
'getUserByLogin')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment