Commit ed08a17c authored by Łukasz Nowak's avatar Łukasz Nowak

Simplify External Authnetication plugin.

By understanding external_login in ERP5UserManager it is not required to repeat
authenticateCredentials logic.

Thanks to this ERP5ExternalAuthenticationPlugin can be used only as credential
extraction plugin.

Extend test suite to prove that enabling ERP5ExternalAuthenticationPlugin does
not impact default scenario.
parent ec48a173
......@@ -26,14 +26,8 @@
#
##############################################################################
from DateTime import DateTime
from zLOG import LOG, PROBLEM
from Products.ERP5Type.Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager,\
newSecurityManager,\
setSecurityManager
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PluggableAuthService.interfaces import plugins
......@@ -41,9 +35,7 @@ from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.permissions import ManageUsers
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Security.ERP5UserManager import ERP5UserManager,\
SUPER_USER, _AuthenticationFailure
from Products.ERP5Security.ERP5UserManager import ERP5UserManager
#Form for new plugin in ZMI
manage_addERP5ExternalAuthenticationPluginForm = PageTemplateFile(
......@@ -120,70 +112,6 @@ class ERP5ExternalAuthenticationPlugin(ERP5UserManager):
return creds
################################
# IAuthenticationPlugin #
################################
security.declarePrivate('authenticateCredentials')
def authenticateCredentials(self, credentials):
"""Authentificate with credentials"""
login = credentials.get('external_login', None)
# Forbidden the usage of the super user.
if login == SUPER_USER:
return None
#Function to allow cache
def _authenticateCredentials(login):
if not login:
return None
#Search the user by his login
user_list = self.getUserByLogin(login)
if len(user_list) != 1:
raise _AuthenticationFailure()
user = user_list[0]
#We need to be super_user
sm = getSecurityManager()
if sm.getUser().getId() != SUPER_USER:
newSecurityManager(self, self.getUser(SUPER_USER))
try:
# get assignment list
assignment_list = [x for x in user.objectValues(portal_type="Assignment") \
if x.getValidationState() == "open"]
valid_assignment_list = []
# check dates if exist
login_date = DateTime()
for assignment in assignment_list:
if assignment.getStartDate() is not None and \
assignment.getStartDate() > login_date:
continue
if assignment.getStopDate() is not None and \
assignment.getStopDate() < login_date:
continue
valid_assignment_list.append(assignment)
# validate
if len(valid_assignment_list) > 0:
return (login,login)
finally:
setSecurityManager(sm)
raise _AuthenticationFailure()
#Cache Method for best performance
_authenticateCredentials = CachingMethod(
_authenticateCredentials,
id='ERP5ExternalAuthenticationPlugin_authenticateCredentials',
cache_factory='erp5_content_short')
try:
return _authenticateCredentials(login=login)
except _AuthenticationFailure:
return None
except StandardError,e:
#Log standard error
LOG('ERP5ExternalAuthenticationPlugin.authenticateCredentials', PROBLEM, str(e))
return None
################################
# Properties for ZMI managment #
################################
......@@ -219,7 +147,6 @@ class ERP5ExternalAuthenticationPlugin(ERP5UserManager):
#List implementation of class
classImplements(ERP5ExternalAuthenticationPlugin,
plugins.IAuthenticationPlugin,
plugins.ILoginPasswordHostExtractionPlugin)
InitializeClass(ERP5ExternalAuthenticationPlugin)
......@@ -118,12 +118,21 @@ class ERP5UserManager(BasePlugin):
o We expect the credentials to be those returned by
ILoginPasswordExtractionPlugin.
"""
login = credentials.get('login')
ignore_password = False
if not login:
# fallback to support plugins using external tools to extract login
# those are not using login/password pair, they just extract login
# from remote system (eg. SSL certificates)
login = credentials.get('external_login')
ignore_password = True
# Forbidden the usage of the super user.
if credentials.get('login') == SUPER_USER:
if login == SUPER_USER:
return None
def _authenticateCredentials(login, password, path):
if not login or not password:
def _authenticateCredentials(login, password, path,
ignore_password=False):
if not login or not (password or ignore_password):
return None
user_list = self.getUserByLogin(login)
......@@ -151,7 +160,7 @@ class ERP5UserManager(BasePlugin):
continue
valid_assignment_list.append(assignment)
if pw_validate(user.getPassword(), password) and \
if (ignore_password or pw_validate(user.getPassword(), password)) and \
len(valid_assignment_list) and user \
.getValidationState() != 'deleted': #user.getCareerRole() == 'internal':
return login, login # use same for user_id and login
......@@ -164,9 +173,10 @@ class ERP5UserManager(BasePlugin):
cache_factory='erp5_content_short')
try:
authentication_result = _authenticateCredentials(
login=credentials.get('login'),
login=login,
password=credentials.get('password'),
path=self.getPhysicalPath())
path=self.getPhysicalPath(),
ignore_password=ignore_password)
except _AuthenticationFailure:
authentication_result = None
......
......@@ -409,6 +409,52 @@ class TestUserManagement(ERP5TypeTestCase):
self.tic()
self.assertEqual(None, person.getReference())
class TestUserManagementExternalAuthentication(TestUserManagement):
def afterSetUp(self):
self.user_id_key = 'openAMid'
# add key authentication PAS plugin
uf = self.portal.acl_users
plugin_id = 'erp5_external_authentication_plugin'
if plugin_id not in uf.objectIds():
uf.manage_addProduct['ERP5Security'].addERP5ExternalAuthenticationPlugin(
id=plugin_id, \
title='ERP5 External Authentication Plugin',\
user_id_key=self.user_id_key,)
getattr(uf, plugin_id).manage_activateInterfaces(
interfaces=['IExtractionPlugin'])
self.stepTic()
def testERP5ExternalAuthenticationPlugin(self):
"""
Make sure that we can grant security using a ERP5 External Authentication Plugin.
"""
reference = 'external_auth_person'
loginable_person = self.getPersonModule().newContent(portal_type='Person',
reference=reference,
password='guest')
assignment = loginable_person.newContent(portal_type='Assignment')
assignment.open()
self.stepTic()
base_url = self.portal.absolute_url(relative=1)
# without key we are Anonymous User so we should be redirected with proper HTML
# status code to login_form
response = self.publish(base_url)
self.assertEqual(response.getStatus(), 302)
# TODO we should not have redirect but output 403 or 404, because
# login process should be provided by an external application.
# self.assertTrue('location' in response.headers.keys())
# self.assertTrue(response.headers['location'].endswith('login_form'))
# view front page we should be logged in if we use authentication key
response = self.publish(base_url, env={self.user_id_key.replace('-', '_').upper():reference})
self.assertEqual(response.getStatus(), 200)
self.assertTrue(reference in response.getBody())
class TestLocalRoleManagement(ERP5TypeTestCase):
"""Tests Local Role Management with ERP5Security.
......@@ -780,50 +826,6 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
base_url, web_page.getReference(), 'ERP5TypeTestCase', ''))
self.assertEqual(response.getStatus(), 200)
def testERP5ExternalAuthenticationPlugin(self):
"""
Make sure that we can grant security using a ERP5 External Authentication Plugin.
"""
user_id_key = 'openAMid'
# add key authentication PAS plugin
portal = self.portal
uf = portal.acl_users
uf.manage_addProduct['ERP5Security'].addERP5ExternalAuthenticationPlugin(
id='erp5_external_authentication_plugin', \
title='ERP5 External Authentication Plugin',\
user_id_key=user_id_key,)
erp5_external_authentication_plugin = getattr(uf, 'erp5_external_authentication_plugin')
erp5_external_authentication_plugin.manage_activateInterfaces(
interfaces=['IExtractionPlugin',
'IAuthenticationPlugin'])
self.stepTic()
reference = 'external_auth_person'
loginable_person = self.getPersonModule().newContent(portal_type='Person',
reference=reference,
password='guest')
assignment = loginable_person.newContent(portal_type='Assignment',
function='another_subcat')
assignment.open()
self.stepTic()
base_url = portal.absolute_url(relative=1)
# without key we are Anonymous User so we should be redirected with proper HTML
# status code to login_form
response = self.publish(base_url)
self.assertEqual(response.getStatus(), 302)
# TODO we should not have redirect but output 403 or 404, because
# login process should be provided by an external application.
# self.assertTrue('location' in response.headers.keys())
# self.assertTrue(response.headers['location'].endswith('login_form'))
# view front page we should be logged in if we use authentication key
response = self.publish(base_url, env={user_id_key.replace('-', '_').upper():reference})
self.assertEqual(response.getStatus(), 200)
self.assertTrue(reference in response.getBody())
def _createZodbUser(self, login, role_list=None):
if role_list is None:
role_list = ['Member', 'Assignee', 'Assignor', 'Author', 'Auditor',
......@@ -910,5 +912,6 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestUserManagement))
suite.addTest(unittest.makeSuite(TestUserManagementExternalAuthentication))
suite.addTest(unittest.makeSuite(TestLocalRoleManagement))
return suite
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